<p>-version 3.26 | <p>-version 3.26 | ||||
<ul> | <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> | </ul> | ||||
<p>-version 3.25 on April 16, 2019 | <p>-version 3.25 on April 16, 2019 |
destdir="${build.classes.dir}" | destdir="${build.classes.dir}" | ||||
debug="on" | debug="on" | ||||
deprecation="on" | deprecation="on" | ||||
source="1.7" | |||||
target="1.7" | |||||
source="1.8" | |||||
target="1.8" | |||||
optimize="off" | optimize="off" | ||||
includeantruntime="true" | includeantruntime="true" | ||||
includes="**"> | includes="**"> |
<artifactId>maven-compiler-plugin</artifactId> | <artifactId>maven-compiler-plugin</artifactId> | ||||
<version>3.2</version> | <version>3.2</version> | ||||
<configuration> | <configuration> | ||||
<source>1.7</source> | |||||
<target>1.7</target> | |||||
<source>1.8</source> | |||||
<target>1.8</target> | |||||
<testSource>11</testSource> | <testSource>11</testSource> | ||||
<testTarget>11</testTarget> | <testTarget>11</testTarget> | ||||
<testCompilerArgument>-parameters</testCompilerArgument> | <testCompilerArgument>-parameters</testCompilerArgument> |
/** | /** | ||||
* Inserts bytecode at the end of the body. | * 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. | * It is not executed when an exception is thrown. | ||||
* | * | ||||
* @param src the source code representing the inserted bytecode. | * @param src the source code representing the inserted bytecode. | ||||
public void insertAfter(String src) | public void insertAfter(String src) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | { | ||||
insertAfter(src, false); | |||||
insertAfter(src, false, false); | |||||
} | } | ||||
/** | /** | ||||
* Inserts bytecode at the end of the body. | * 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. | * @param src the source code representing the inserted bytecode. | ||||
* It must be a single statement or block. | * It must be a single statement or block. | ||||
*/ | */ | ||||
public void insertAfter(String src, boolean asFinally) | public void insertAfter(String src, boolean asFinally) | ||||
throws CannotCompileException | 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; | CtClass cc = declaringClass; | ||||
cc.checkModify(); | cc.checkModify(); | ||||
if (c == Opcode.ARETURN || c == Opcode.IRETURN | if (c == Opcode.ARETURN || c == Opcode.IRETURN | ||||
|| c == Opcode.FRETURN || c == Opcode.LRETURN | || c == Opcode.FRETURN || c == Opcode.LRETURN | ||||
|| c == Opcode.DRETURN || c == Opcode.RETURN) { | || 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; | advicePos = iterator.getCodeLength() - adviceLen; | ||||
handlerLen = advicePos - handlerPos; | |||||
noReturn = false; | |||||
handlerPos = advicePos - handlerLen; | |||||
} | } | ||||
insertGoto(iterator, advicePos, pos); | |||||
advicePos = iterator.getCodeLength() - adviceLen; | |||||
handlerPos = advicePos - handlerLen; | |||||
} | } | ||||
} | } | ||||
protected byte[] bytecode; | protected byte[] bytecode; | ||||
protected int endPos; | protected int endPos; | ||||
protected int currentPos; | protected int currentPos; | ||||
protected int mark; | |||||
protected int mark, mark2; | |||||
protected CodeIterator(CodeAttribute ca) { | protected CodeIterator(CodeAttribute ca) { | ||||
codeAttr = ca; | codeAttr = ca; | ||||
* Moves to the first instruction. | * Moves to the first instruction. | ||||
*/ | */ | ||||
public void begin() { | public void begin() { | ||||
currentPos = mark = 0; | |||||
currentPos = mark = mark2 = 0; | |||||
endPos = getCodeLength(); | endPos = getCodeLength(); | ||||
} | } | ||||
mark = index; | 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 | * Gets the index of the position of the mark set by | ||||
* <code>setMark</code>. | * <code>setMark</code>. | ||||
*/ | */ | ||||
public int getMark() { return mark; } | 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. | * Returns a Code attribute read with this iterator. | ||||
*/ | */ | ||||
if (mark > pos || (mark == pos && exclusive)) | if (mark > pos || (mark == pos && exclusive)) | ||||
mark += length2; | mark += length2; | ||||
if (mark2 > pos || (mark2 == pos && exclusive)) | |||||
mark2 += length2; | |||||
} | } | ||||
codeAttr.setCode(c); | codeAttr.setCode(c); | ||||
static class Pointers { | static class Pointers { | ||||
int cursor; | int cursor; | ||||
int mark0, mark; | |||||
int mark0, mark, mark2; | |||||
ExceptionTable etable; | ExceptionTable etable; | ||||
LineNumberAttribute line; | LineNumberAttribute line; | ||||
LocalVariableAttribute vars, types; | LocalVariableAttribute vars, types; | ||||
StackMapTable stack; | StackMapTable stack; | ||||
StackMap stack2; | 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; | cursor = cur; | ||||
mark = m; | mark = m; | ||||
mark2 = m2; | |||||
mark0 = m0; | mark0 = m0; | ||||
etable = et; // non null | etable = et; // non null | ||||
line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); | line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); | ||||
if (where < mark || (where == mark && exclusive)) | if (where < mark || (where == mark && exclusive)) | ||||
mark += gapLength; | mark += gapLength; | ||||
if (where < mark2 || (where == mark2 && exclusive)) | |||||
mark2 += gapLength; | |||||
if (where < mark0 || (where == mark0 && exclusive)) | if (where < mark0 || (where == mark0 && exclusive)) | ||||
mark0 += gapLength; | mark0 += gapLength; | ||||
CodeAttribute ca, CodeAttribute.LdcEntry ldcs) | CodeAttribute ca, CodeAttribute.LdcEntry ldcs) | ||||
throws BadBytecode | 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); | List<Branch> jumps = makeJumpList(code, code.length, pointers); | ||||
while (ldcs != null) { | while (ldcs != null) { | ||||
addLdcW(ldcs, jumps); | addLdcW(ldcs, jumps); | ||||
if (gapLength <= 0) | if (gapLength <= 0) | ||||
return code; | 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); | List<Branch> jumps = makeJumpList(code, code.length, pointers); | ||||
byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers); | byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers); | ||||
currentPos = pointers.cursor; | currentPos = pointers.cursor; | ||||
mark = pointers.mark; | mark = pointers.mark; | ||||
mark2 = pointers.mark2; | |||||
int where2 = pointers.mark0; | int where2 = pointers.mark0; | ||||
if (where2 == currentPos && !exclusive) | if (where2 == currentPos && !exclusive) | ||||
currentPos += gapLength; | currentPos += gapLength; |
Object obj = make(cc.getName()); | Object obj = make(cc.getName()); | ||||
assertEquals(1, invoke(obj, "run")); | 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")); | |||||
} | |||||
} | } |
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; | |||||
} | |||||
} |