@@ -283,7 +283,7 @@ see javassist.Dump. | |||
<p>-version 3.26 | |||
<ul> | |||
<li>GitHub Issue #270 (PR #272), Issue #265 (PR #267), Issue #271 and #222. | |||
<li>GitHub Issue #270 (PR #272), #265 (PR #267), #271, #222, and #275. | |||
</ul> | |||
<p>-version 3.25 on April 16, 2019 |
@@ -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="**"> |
@@ -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> |
@@ -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,30 @@ 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. | |||
* @since 3.26 | |||
*/ | |||
public void insertAfter(String src, boolean asFinally, boolean redundant) | |||
throws CannotCompileException | |||
{ | |||
CtClass cc = declaringClass; | |||
cc.checkModify(); | |||
@@ -878,18 +902,50 @@ 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 (noReturn) { | |||
// finally clause for normal termination | |||
adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo); | |||
handlerPos = iterator.append(b.get()); | |||
iterator.append(b.getExceptionTable(), handlerPos); | |||
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); | |||
handlerPos = iterator.append(b.get()); | |||
iterator.append(b.getExceptionTable(), handlerPos); | |||
advicePos = iterator.getCodeLength() - adviceLen; | |||
handlerLen = advicePos - handlerPos; | |||
noReturn = false; | |||
} | |||
insertGoto(iterator, advicePos, pos); | |||
advicePos = iterator.getCodeLength() - adviceLen; | |||
handlerLen = advicePos - handlerPos; | |||
noReturn = false; | |||
handlerPos = advicePos - handlerLen; | |||
} | |||
insertGoto(iterator, advicePos, pos); | |||
advicePos = iterator.getCodeLength() - adviceLen; | |||
handlerPos = advicePos - handlerLen; | |||
} | |||
} | |||
@@ -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; |
@@ -546,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(1, invoke(obj, "run")); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package test5; | |||
public class InsertAfter { | |||
public int run() { | |||
return foo(7) + bar(20); | |||
} | |||
public int foo(int k) { | |||
if (k > 0) | |||
if (k > 10) | |||
return k + 1; | |||
else | |||
return k * 10; | |||
else | |||
return k * 100; | |||
} | |||
public int bar(int k) { | |||
if (k > 0) | |||
try { | |||
if (k > 10) | |||
return k + 1; | |||
else | |||
return k * 10; | |||
} | |||
catch (Exception e) { | |||
if (k > 0) | |||
return k * 1000; | |||
else | |||
throw e; | |||
} | |||
else | |||
return k * 100; | |||
} | |||
} |