Skip to content
Commits on Source (4)
......@@ -281,6 +281,11 @@ see javassist.Dump.
<h2>Changes</h2>
<p>-version 3.26 on October 3, 2019
<ul>
<li>GitHub Issue #270 (PR #272), #265 (PR #267), #271, #222, and #275.
</ul>
<p>-version 3.25 on April 16, 2019
<ul>
<li>GitHub Issue #72 (PR #231), #241, #242 (PR #243), PR #244,
......
......@@ -6,7 +6,7 @@
<project name="javassist" default="jar" basedir=".">
<property name="dist-version" value="javassist-3.25.0-GA"/>
<property name="dist-version" value="javassist-3.26.0-GA"/>
<property environment="env"/>
<property name="target.jar" value="javassist.jar"/>
......@@ -79,8 +79,8 @@
destdir="${build.classes.dir}"
debug="on"
deprecation="on"
source="1.7"
target="1.7"
source="1.8"
target="1.8"
optimize="off"
includeantruntime="true"
includes="**">
......
javassist (1:3.26.0-1) unstable; urgency=medium
* Team upload.
* New upstream release
* Standards-Version updated to 4.5.0
-- Emmanuel Bourg <ebourg@apache.org> Mon, 27 Jan 2020 10:17:05 +0100
javassist (1:3.25.0-1) unstable; urgency=medium
* Team upload.
......
......@@ -12,7 +12,7 @@ Build-Depends:
libmaven-bundle-plugin-java,
libmaven-javadoc-plugin-java,
maven-debian-helper (>= 2.1)
Standards-Version: 4.4.0
Standards-Version: 4.5.0
Vcs-Git: https://salsa.debian.org/java-team/javassist.git
Vcs-Browser: https://salsa.debian.org/java-team/javassist
Homepage: http://jboss-javassist.github.io/javassist/
......
......@@ -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.25.0-GA</version>
<version>3.26.0-GA</version>
<name>Javassist</name>
<url>http://www.javassist.org/</url>
......@@ -151,8 +151,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<testSource>11</testSource>
<testTarget>11</testTarget>
<testCompilerArgument>-parameters</testCompilerArgument>
......
Specification-Title: Javassist
Specification-Vendor: Shigeru Chiba, www.javassist.org
Specification-Version: 3.25.0-GA
Specification-Version: 3.26.0-GA
Main-Class: javassist.CtClass
Automatic-Module-Name: org.javassist
......@@ -113,6 +113,14 @@ public class ClassPool {
*/
public static boolean releaseUnmodifiedClassFile = true;
/**
* If true, the contents of a jar file are cached after the jar
* file is opened.
*
* <p>The initial value is true.
*/
public static boolean cacheOpenedJarFile = true; // see ClassPoolTail.JarClassPath#openClassfile(String)
protected ClassPoolTail source;
protected ClassPool parent;
protected Hashtable classes; // should be synchronous
......
......@@ -159,10 +159,14 @@ final class JarClassPath implements ClassPath {
URL jarURL = find(classname);
if (null != jarURL)
try {
if (ClassPool.cacheOpenedJarFile)
return jarURL.openConnection().getInputStream();
else {
java.net.URLConnection con = jarURL.openConnection();
con.setUseCaches(false);
return con.getInputStream();
}
}
catch (IOException e) {
throw new NotFoundException("broken jar file?: "
+ classname);
......
......@@ -812,7 +812,7 @@ public abstract class CtBehavior extends CtMember {
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return insturction.
* The bytecode is inserted just before every return instruction.
* It is not executed when an exception is thrown.
*
* @param src the source code representing the inserted bytecode.
......@@ -821,12 +821,12 @@ public abstract class CtBehavior extends CtMember {
public void insertAfter(String src)
throws CannotCompileException
{
insertAfter(src, false);
insertAfter(src, false, false);
}
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return insturction.
* The bytecode is inserted just before every return instruction.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
......@@ -838,6 +838,35 @@ public abstract class CtBehavior extends CtMember {
*/
public void insertAfter(String src, boolean asFinally)
throws CannotCompileException
{
insertAfter(src, asFinally, false);
}
/**
* Inserts bytecode at the end of the body.
* The bytecode is inserted just before every return instruction.
*
* @param src the source code representing the inserted bytecode.
* It must be a single statement or block.
* @param asFinally true if the inserted bytecode is executed
* not only when the control normally returns
* but also when an exception is thrown.
* If this parameter is true, the inserted code cannot
* access local variables.
* @param redundant if true, redundant bytecode will be generated.
* the redundancy is necessary when some compilers (Kotlin?)
* generate the original bytecode.
* The other <code>insertAfter</code> methods calls this method
* with <code>false</code> for this parameter.
* A tip is to pass <code>this.getDeclaringClass().isKotlin()</code>
* to this parameter.
*
* @see CtClass#isKotlin()
* @see #getDeclaringClass()
* @since 3.26
*/
public void insertAfter(String src, boolean asFinally, boolean redundant)
throws CannotCompileException
{
CtClass cc = declaringClass;
cc.checkModify();
......@@ -878,6 +907,36 @@ public abstract class CtBehavior extends CtMember {
if (c == Opcode.ARETURN || c == Opcode.IRETURN
|| c == Opcode.FRETURN || c == Opcode.LRETURN
|| c == Opcode.DRETURN || c == Opcode.RETURN) {
if (redundant) {
iterator.setMark2(handlerPos);
Bytecode bcode;
Javac jvc;
int retVarNo;
if (noReturn) {
noReturn = false;
bcode = b;
jvc = jv;
retVarNo = varNo;
}
else {
bcode = new Bytecode(pool, 0, retAddr + 1);
bcode.setStackDepth(ca.getMaxStack() + 1);
jvc = new Javac(bcode, cc);
int nvars2 = jvc.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
jvc.recordParamNames(ca, nvars2);
retVarNo = jvc.recordReturnType(rtype, true);
jvc.recordLocalVariables(ca, 0);
}
int adviceLen2 = insertAfterAdvice(bcode, jvc, src, pool, rtype, retVarNo);
int offset = iterator.append(bcode.get());
iterator.append(bcode.getExceptionTable(), offset);
int advicePos2 = iterator.getCodeLength() - adviceLen2;
insertGoto(iterator, advicePos2, pos);
handlerPos = iterator.getMark2();
}
else {
if (noReturn) {
// finally clause for normal termination
adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo);
......@@ -887,11 +946,13 @@ public abstract class CtBehavior extends CtMember {
handlerLen = advicePos - handlerPos;
noReturn = false;
}
insertGoto(iterator, advicePos, pos);
advicePos = iterator.getCodeLength() - adviceLen;
handlerPos = advicePos - handlerLen;
}
}
}
if (noReturn) {
handlerPos = iterator.append(b.get());
......
......@@ -69,7 +69,7 @@ public abstract class CtClass {
/**
* The version number of this release.
*/
public static final String version = "3.25.0-GA";
public static final String version = "3.26.0-GA";
/**
* Prints the version number and the copyright notice.
......@@ -332,6 +332,14 @@ public abstract class CtClass {
return false;
}
/**
* Returns <code>true</code> if this object represents a Kotlin class.
* @since 3.26
*/
public boolean isKotlin() {
return hasAnnotation("kotlin.Metadata");
}
/**
* If this object represents an array, this method returns the component
* type of the array. Otherwise, it returns <code>null</code>.
......
......@@ -700,6 +700,24 @@ public class ClassFileWriter {
return num++;
}
/**
* Adds a new <code>CONSTANT_Dynamic_info</code>
* structure.
*
* @param bootstrap <code>bootstrap_method_attr_index</code>.
* @param nameAndTypeInfo <code>name_and_type_index</code>.
* @return the index of the added entry.
*
* @since 3.26
*/
public int addDynamicInfo(int bootstrap,
int nameAndTypeInfo) {
output.write(DynamicInfo.tag);
output.writeShort(bootstrap);
output.writeShort(nameAndTypeInfo);
return num++;
}
/**
* Adds a new <code>CONSTANT_String_info</code>
* structure.
......
......@@ -52,7 +52,7 @@ public class CodeIterator implements Opcode {
protected byte[] bytecode;
protected int endPos;
protected int currentPos;
protected int mark;
protected int mark, mark2;
protected CodeIterator(CodeAttribute ca) {
codeAttr = ca;
......@@ -64,7 +64,7 @@ public class CodeIterator implements Opcode {
* Moves to the first instruction.
*/
public void begin() {
currentPos = mark = 0;
currentPos = mark = mark2 = 0;
endPos = getCodeLength();
}
......@@ -98,6 +98,20 @@ public class CodeIterator implements Opcode {
mark = index;
}
/**
* Sets a mark to the bytecode at the given index.
* The mark can be used to track the position of that bytecode
* when code blocks are inserted.
* If a code block is inclusively inserted at the position of the
* bytecode, the mark is set to the inserted code block.
*
* @see #getMark2()
* @since 3.26
*/
public void setMark2(int index) {
mark2 = index;
}
/**
* Gets the index of the position of the mark set by
* <code>setMark</code>.
......@@ -108,6 +122,16 @@ public class CodeIterator implements Opcode {
*/
public int getMark() { return mark; }
/**
* Gets the index of the position of the mark set by
* <code>setMark2</code>.
*
* @return the index of the position.
* @see #setMark2(int)
* @since 3.26
*/
public int getMark2() { return mark2; }
/**
* Returns a Code attribute read with this iterator.
*/
......@@ -646,6 +670,9 @@ public class CodeIterator implements Opcode {
if (mark > pos || (mark == pos && exclusive))
mark += length2;
if (mark2 > pos || (mark2 == pos && exclusive))
mark2 += length2;
}
codeAttr.setCode(c);
......@@ -1012,16 +1039,17 @@ public class CodeIterator implements Opcode {
static class Pointers {
int cursor;
int mark0, mark;
int mark0, mark, mark2;
ExceptionTable etable;
LineNumberAttribute line;
LocalVariableAttribute vars, types;
StackMapTable stack;
StackMap stack2;
Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) {
Pointers(int cur, int m, int m2, int m0, ExceptionTable et, CodeAttribute ca) {
cursor = cur;
mark = m;
mark2 = m2;
mark0 = m0;
etable = et; // non null
line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
......@@ -1038,6 +1066,9 @@ public class CodeIterator implements Opcode {
if (where < mark || (where == mark && exclusive))
mark += gapLength;
if (where < mark2 || (where == mark2 && exclusive))
mark2 += gapLength;
if (where < mark0 || (where == mark0 && exclusive))
mark0 += gapLength;
......@@ -1074,7 +1105,7 @@ public class CodeIterator implements Opcode {
CodeAttribute ca, CodeAttribute.LdcEntry ldcs)
throws BadBytecode
{
Pointers pointers = new Pointers(0, 0, 0, etable, ca);
Pointers pointers = new Pointers(0, 0, 0, 0, etable, ca);
List<Branch> jumps = makeJumpList(code, code.length, pointers);
while (ldcs != null) {
addLdcW(ldcs, jumps);
......@@ -1118,11 +1149,12 @@ public class CodeIterator implements Opcode {
if (gapLength <= 0)
return code;
Pointers pointers = new Pointers(currentPos, mark, where, etable, ca);
Pointers pointers = new Pointers(currentPos, mark, mark2, where, etable, ca);
List<Branch> jumps = makeJumpList(code, code.length, pointers);
byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers);
currentPos = pointers.cursor;
mark = pointers.mark;
mark2 = pointers.mark2;
int where2 = pointers.mark0;
if (where2 == currentPos && !exclusive)
currentPos += gapLength;
......
......@@ -105,7 +105,12 @@ public final class ConstPool
public static final int CONST_MethodType = MethodTypeInfo.tag;
/**
* <code>CONSTANT_MethodHandle</code>
* <code>CONSTANT_Dynamic</code>
*/
public static final int CONST_Dynamic = DynamicInfo.tag;
/**
* <code>CONSTANT_InvokeDynamic</code>
*/
public static final int CONST_InvokeDynamic = InvokeDynamicInfo.tag;
......@@ -748,7 +753,7 @@ public final class ConstPool
* indirectly specified by the given index.
*
* @param index an index to a <code>CONSTANT_InvokeDynamic_info</code>.
* @return the descriptor of the method.
* @return the descriptor of the bootstrap method.
* @since 3.17
*/
public String getInvokeDynamicType(int index)
......@@ -762,6 +767,52 @@ public final class ConstPool
return getUtf8Info(n.typeDescriptor);
}
/**
* Reads the <code>bootstrap_method_attr_index</code> field of the
* <code>CONSTANT_Dynamic_info</code> structure
* at the given index.
*
* @since 3.26
*/
public int getDynamicBootstrap(int index)
{
DynamicInfo iv = (DynamicInfo)getItem(index);
return iv.bootstrap;
}
/**
* Reads the <code>name_and_type_index</code> field of the
* <code>CONSTANT_Dynamic_info</code> structure
* at the given index.
*
* @since 3.26
*/
public int getDynamicNameAndType(int index)
{
DynamicInfo iv = (DynamicInfo)getItem(index);
return iv.nameAndType;
}
/**
* Reads the <code>descriptor_index</code> field of the
* <code>CONSTANT_NameAndType_info</code> structure
* indirectly specified by the given index.
*
* @param index an index to a <code>CONSTANT_Dynamic_info</code>.
* @return the descriptor of the bootstrap method.
* @since 3.26
*/
public String getDynamicType(int index)
{
DynamicInfo iv = (DynamicInfo)getItem(index);
if (iv == null)
return null;
NameAndTypeInfo n = (NameAndTypeInfo)getItem(iv.nameAndType);
if(n == null)
return null;
return getUtf8Info(n.typeDescriptor);
}
/**
* Reads the <code>name_index</code> field of the
* <code>CONSTANT_Module_info</code> structure at the given index.
......@@ -1195,6 +1246,18 @@ public final class ConstPool
return addItem(new InvokeDynamicInfo(bootstrap, nameAndType, numOfItems));
}
/**
* Adds a new <code>CONSTANT_Dynamic_info</code> structure.
*
* @param bootstrap <code>bootstrap_method_attr_index</code>.
* @param nameAndType <code>name_and_type_index</code>.
* @return the index of the added entry.
* @since 3.26
*/
public int addDynamicInfo(int bootstrap, int nameAndType) {
return addItem(new DynamicInfo(bootstrap, nameAndType, numOfItems));
}
/**
* Adds a new <code>CONSTANT_Module_info</code>
* @param nameIndex the index of the Utf8 entry.
......@@ -1342,6 +1405,9 @@ public final class ConstPool
case MethodTypeInfo.tag : // 16
info = new MethodTypeInfo(in, numOfItems);
break;
case DynamicInfo.tag : // 17
info = new DynamicInfo(in, numOfItems);
break;
case InvokeDynamicInfo.tag : // 18
info = new InvokeDynamicInfo(in, numOfItems);
break;
......@@ -2322,6 +2388,63 @@ class InvokeDynamicInfo extends ConstInfo
}
}
class DynamicInfo extends ConstInfo {
static final int tag = 17;
int bootstrap, nameAndType;
public DynamicInfo(int bootstrapMethod, int ntIndex, int index) {
super(index);
bootstrap = bootstrapMethod;
nameAndType = ntIndex;
}
public DynamicInfo(DataInputStream in, int index) throws IOException {
super(index);
bootstrap = in.readUnsignedShort();
nameAndType = in.readUnsignedShort();
}
@Override
public int hashCode() {
return (bootstrap << 16) ^ nameAndType;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DynamicInfo) {
DynamicInfo iv = (DynamicInfo)obj;
return iv.bootstrap == bootstrap && iv.nameAndType == nameAndType;
}
return false;
}
@Override
public int getTag() {
return tag;
}
@Override
public int copy(ConstPool src, ConstPool dest, Map<String, String> map) {
return dest.addDynamicInfo(bootstrap, src.getItem(nameAndType).copy(src, dest, map));
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeByte(tag);
out.writeShort(bootstrap);
out.writeShort(nameAndType);
}
@Override
public void print(PrintWriter out) {
out.print("Dynamic #");
out.print(bootstrap);
out.print(", name&type #");
out.println(nameAndType);
}
}
class ModuleInfo extends ConstInfo
{
static final int tag = 19;
......
......@@ -619,6 +619,18 @@ public class MemberCodeGen extends CodeGen {
aload0pos, found);
}
private boolean isFromSameDeclaringClass(CtClass outer, CtClass inner) {
try {
while (outer != null) {
if (isEnclosing(outer, inner))
return true;
outer = outer.getDeclaringClass();
}
}
catch (NotFoundException e) {}
return false;
}
private void atMethodCallCore2(CtClass targetClass, String mname,
boolean isStatic, boolean isSpecial,
int aload0pos,
......@@ -636,19 +648,8 @@ public class MemberCodeGen extends CodeGen {
throw new CompileError("no such constructor: " + targetClass.getName());
if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
boolean isNested = false;
if (declClass.getClassFile().getMajorVersion() >= ClassFile.JAVA_11) {
try {
CtClass[] nestedClasses = declClass.getNestedClasses();
for (int i = 0; i < nestedClasses.length; i++) {
if (thisClass == nestedClasses[i]) {
isNested = true;
break;
}
}
} catch (NotFoundException ignored) { }
}
if (!isNested) {
if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_11
|| !isFromSameDeclaringClass(declClass, thisClass)) {
desc = getAccessibleConstructor(desc, declClass, minfo);
bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
}
......
......@@ -539,7 +539,7 @@ public class MemberResolver implements TokenId {
if (intfs[i].getName().equals(interfaceName))
return intfs[i];
} catch (NotFoundException e) {}
throw new CompileError("cannot find the super inetrface " + interfaceName
throw new CompileError("cannot find the super interface " + interfaceName
+ " of " + c.getName());
}
......
......@@ -49,6 +49,7 @@ import javassist.bytecode.ExceptionsAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.StackMapTable;
/*
......@@ -186,6 +187,8 @@ public class ProxyFactory {
private String basename;
private String superName;
private Class<?> thisClass;
private String genericSignature;
/**
* per factory setting initialised from current setting for useCache but able to be reset before each create call
*/
......@@ -389,6 +392,7 @@ public class ProxyFactory {
signatureMethods = null;
hasGetHandler = false;
thisClass = null;
genericSignature = null;
writeDirectory = null;
factoryUseCache = useCache;
factoryWriteReplace = useWriteReplace;
......@@ -435,6 +439,34 @@ public class ProxyFactory {
signature = null;
}
/**
* Sets the generic signature for the proxy class.
* <p>For example,
*
* <pre>class NullPtr&lt;T&gt; {
* T get() { return null; }
* }
* </pre>
*
* <p>The following code makes a proxy for <code>NullPtr&lt;String&gt;</code>:
*
* <pre>ProxyFactory factory = new ProxyFactory();
* factory.setSuperclass(NullPtr.class);
* factory.setGenericSignature("LNullPtr&lt;Ljava/lang/String;&gt;;");
* NullPtr&lt;?&gt; np = (NullPtr&lt;?&gt;)factory.create(null, null);
*java.lang.reflect.Type[] x = ((java.lang.reflect.ParameterizedType)np.getClass().getGenericSuperclass())
* .getActualTypeArguments();
* // x[0] == String.class
* </pre>
*
* @param sig a generic signature.
* @see javassist.CtClass#setGenericSignature(String)
* @since 3.26
*/
public void setGenericSignature(String sig) {
genericSignature = sig;
}
/**
* Generates a proxy class using the current filter.
* The module or package where a proxy class is created
......@@ -885,6 +917,11 @@ public class ProxyFactory {
finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
cf.addField(finfo4);
if (genericSignature != null) {
SignatureAttribute sa = new SignatureAttribute(pool, genericSignature);
cf.addAttribute(sa);
}
// HashMap allMethods = getMethods(superClass, interfaces);
// int size = allMethods.size();
makeConstructors(classname, cf, pool, classname);
......
......@@ -44,9 +44,11 @@ public class Test {
final CtClass ctClass = classPool.get(INVALID_STACK_MAP_FRAME);
final CtMethod method = ctClass.getDeclaredMethod("bytecodeVerifyError");
method.addLocalVariable("test_localVariable", CtClass.intType);
method.insertBefore("{ test_localVariable = 1; }");
ctClass.debugWriteFile();
// method.addLocalVariable("test_localVariable", CtClass.intType);
method.insertBefore("{ java.math.BigDecimal d = null; d.divide(d); }");
//ctClass.debugWriteFile();
System.out.println("ok");
ctClass.writeFile();
Class<?> cc = ctClass.toClass();
System.out.println(cc.getName());
InvalidStackMapFrame obj = (InvalidStackMapFrame)cc.getDeclaredConstructor().newInstance();
......
......@@ -490,6 +490,33 @@ public class JvstTest5 extends JvstTestRoot {
}
}
public void testNestPrivateConstructor2() throws Exception {
CtClass cc = sloader.get("test5.NestHost4$InnerClass1");
cc.instrument(new ExprEditor() {
public void edit(NewExpr e) throws CannotCompileException {
String code = "$_ = $proceed($$);";
e.replace(code);
}
});
cc.writeFile();
cc = sloader.get("test5.NestHost4$InnerClass1$InnerClass5");
cc.instrument(new ExprEditor() {
public void edit(NewExpr e) throws CannotCompileException {
String code = "$_ = $proceed($$);";
e.replace(code);
}
});
cc.writeFile();
try {
Class<?> nestHost4Class = cloader.loadClass("test5.NestHost4");
nestHost4Class.getDeclaredConstructor().newInstance();
} catch (Exception ex) {
ex.printStackTrace();
fail("it should be able to access the private constructor of the nest host");
}
}
public void testSwitchCaseWithStringConstant2() throws Exception {
CtClass cc = sloader.makeClass("test5.SwitchCase2");
cc.addMethod(CtNewMethod.make(
......@@ -519,4 +546,16 @@ public class JvstTest5 extends JvstTestRoot {
Object obj = make(cc.getName());
assertEquals(1, invoke(obj, "run"));
}
// Issue #275
public void testRedundantInsertAfter() throws Exception {
CtClass cc = sloader.get(test5.InsertAfter.class.getName());
CtMethod m = cc.getDeclaredMethod("foo");
m.insertAfter("{ $_ += 1; }", false, true);
CtMethod m2 = cc.getDeclaredMethod("bar");
m2.insertAfter("{ $_ += 1; }", true, true);
cc.writeFile();
Object obj = make(cc.getName());
assertEquals(71 + 22, invoke(obj, "run"));
}
}
package javassist.proxyfactory;
class GenSig<T> {
}
......@@ -152,4 +152,15 @@ public class ProxyFactoryTest extends TestCase {
}
});
}
// Issue #263
public void testGenericSignature() throws Exception {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(GenSig.class);
factory.setGenericSignature("Ljavassist/proxyfactory/GenSig<Ljava/lang/Integer;>;");
GenSig gs = (GenSig)factory.create(null, null);
java.lang.reflect.Type[] x = ((java.lang.reflect.ParameterizedType)gs.getClass().getGenericSuperclass())
.getActualTypeArguments();
assertEquals(Integer.class, x[0]);
}
}