Basic implementationpull/436/head
@@ -6,7 +6,7 @@ | |||
<project name="javassist" default="jar" basedir="."> | |||
<property name="dist-version" value="javassist-3.29.2-GA"/> | |||
<property name="dist-version" value="javassist-3.30.0-GA-SNAPSHOT"/> | |||
<property environment="env"/> | |||
<property name="target.jar" value="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.29.2-GA</version> | |||
<version>3.30.0-GA-SNAPSHOT</version> | |||
<name>Javassist</name> | |||
<url>http://www.javassist.org/</url> | |||
@@ -20,6 +20,7 @@ import javassist.bytecode.AccessFlag; | |||
import javassist.bytecode.AnnotationsAttribute; | |||
import javassist.bytecode.AttributeInfo; | |||
import javassist.bytecode.BadBytecode; | |||
import javassist.bytecode.ByteArray; | |||
import javassist.bytecode.Bytecode; | |||
import javassist.bytecode.CodeAttribute; | |||
import javassist.bytecode.CodeIterator; | |||
@@ -1309,4 +1310,82 @@ public abstract class CtBehavior extends CtMember { | |||
throw new CannotCompileException(e); | |||
} | |||
} | |||
/** | |||
* Inserts bytecode after the local variable specified by the type and position | |||
* in the body. | |||
* | |||
* @param type the type of the variable. | |||
* @param pos the position of the local variable with the same type. The | |||
* bytecode is inserted at the beginning of the code after the | |||
* local variable specified by the type and this position. | |||
* @param addName add or change the local variable name. If the value of addName | |||
* is null it does nothing. | |||
* @param src the source code representing the inserted bytecode. It must be | |||
* a single statement or block. | |||
* @since 3.30 | |||
*/ | |||
public void insertAfterLocalVariable(CtClass type, int pos, String addName, String src) | |||
throws CannotCompileException { | |||
ConstPool cp = methodInfo.getConstPool(); | |||
CodeAttribute ca = methodInfo.getCodeAttribute(); | |||
if (ca == null) | |||
throw new CannotCompileException("no method body"); | |||
LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); | |||
if (va == null) | |||
throw new CannotCompileException("no local variable"); | |||
CtClass cc = declaringClass; | |||
cc.checkModify(); | |||
String desc = Descriptor.of(type); | |||
int i = 0; | |||
for (int j : va.entryListOrderedByIndex()) { | |||
if (va.descriptor(j).equals(desc)) { | |||
if (i != pos) { | |||
i++; | |||
continue; | |||
} | |||
if (addName != null) { | |||
ByteArray.write16bit(cp.addUtf8Info(addName), va.get(), j * 10 + 6); | |||
} | |||
int index = va.startPc(j); | |||
CodeIterator iterator = ca.iterator(); | |||
Javac jv = new Javac(cc); | |||
try { | |||
jv.recordLocalVariables(ca, index); | |||
jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); | |||
jv.setMaxLocals(ca.getMaxLocals()); | |||
jv.compileStmnt(src); | |||
Bytecode b = jv.getBytecode(); | |||
int locals = b.getMaxLocals(); | |||
int stack = b.getMaxStack(); | |||
ca.setMaxLocals(locals); | |||
/* | |||
* We assume that there is no values in the operand stack at the position where | |||
* the bytecode is inserted. | |||
*/ | |||
if (stack > ca.getMaxStack()) | |||
ca.setMaxStack(stack); | |||
index = iterator.insertAt(index, b.get()); | |||
iterator.insert(b.getExceptionTable(), index); | |||
methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); | |||
return; | |||
} catch (NotFoundException e) { | |||
throw new CannotCompileException(e); | |||
} catch (CompileError e) { | |||
throw new CannotCompileException(e); | |||
} catch (BadBytecode e) { | |||
throw new CannotCompileException(e); | |||
} | |||
} | |||
} | |||
throw new CannotCompileException( | |||
String.format("no local variable with this type %s and position %s", new Object[] { type, pos })); | |||
} | |||
} |
@@ -69,7 +69,7 @@ public abstract class CtClass { | |||
/** | |||
* The version number of this release. | |||
*/ | |||
public static final String version = "3.29.2-GA"; | |||
public static final String version = "3.30.0-GA-SNAPSHOT"; | |||
/** | |||
* Prints the version number and the copyright notice. |
@@ -18,6 +18,9 @@ package javassist.bytecode; | |||
import java.io.DataInputStream; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
@@ -351,4 +354,31 @@ public class LocalVariableAttribute extends AttributeInfo { | |||
LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { | |||
return new LocalVariableAttribute(cp, tag, dest); | |||
} | |||
/** | |||
* Undocumented method. Do not use; internal-use only. | |||
* | |||
* Returns a list of entries in the correct order according to the local | |||
* variable index. This method is for internal use only as the order may be | |||
* wrong with local variables with different type descriptor. | |||
* | |||
* @see #index(int) | |||
* @since 3.30 | |||
*/ | |||
public List<Integer> entryListOrderedByIndex() { | |||
int n = tableLength(); | |||
Integer[] entries = new Integer[n]; | |||
for (int i = 0; i < n; i++) { | |||
int index = index(i); | |||
while (index < n && entries[index] != null) { | |||
index++; | |||
} | |||
if (index < n) { | |||
entries[index] = i; | |||
} | |||
} | |||
return Collections.unmodifiableList(Arrays.asList(entries)); | |||
} | |||
} |
@@ -593,4 +593,27 @@ public class JvstTest5 extends JvstTestRoot { | |||
} | |||
catch (CannotCompileException e) {} | |||
} | |||
// Issue #430 | |||
public void testInsertAfterLocalVariable() throws Exception { | |||
CtClass cc = sloader.get("test5.InsertLocalVariable"); | |||
CtMethod m1 = cc.getDeclaredMethod("foo"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 0, null, "$1++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 1, null, "$2++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 2, null, "$3++;"); | |||
m1.insertAfterLocalVariable(CtClass.booleanType, 0, null, "bI = true;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 3, "iL", "iL++;"); | |||
m1.insertAfterLocalVariable(CtClass.booleanType, 1, null, "bJ = false;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 4, "iM", "iM++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 5, "iN", "iN++;"); | |||
m1.insertAfterLocalVariable(CtClass.booleanType, 2, null, "bK = true;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 6, "iO", "iO++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 7, "iP", "iP++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 8, "iQ", "iQ++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 9, "iR", "iR++;"); | |||
m1.insertAfterLocalVariable(CtClass.intType, 10, "iS", "iS++;"); | |||
cc.writeFile(); | |||
Object obj = make(cc.getName()); | |||
assertEquals(12, invoke(obj, "test")); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
package test5; | |||
public class InsertLocalVariable { | |||
int k; | |||
public InsertLocalVariable() { | |||
k = 1; | |||
} | |||
public int test() { | |||
foo(0, 0, 0); | |||
return k; | |||
} | |||
public void foo(int i /* 0 */, int j /* 1 */, int k /* 2 */) { | |||
boolean bI = false; /* 0 */ | |||
int l = 0; /* 3 */ | |||
if (l == 0) { | |||
this.k = -1; | |||
return; | |||
} | |||
try { | |||
boolean bJ = true; /* 1 */ | |||
if (!bJ) { | |||
throw new Exception(); | |||
} | |||
} catch (Exception e /* 0 */) { | |||
int m = 0; /* 4 */ | |||
l += m; | |||
if (bI) { | |||
int n = 0; /* 5 */ | |||
l += n; | |||
} | |||
for (boolean bK = false /* 2 */; l == 3;) { | |||
if (bK) { | |||
int o = 0; /* 6 */ | |||
l += o; | |||
} | |||
} | |||
while (l == 4) { | |||
int p = 0; /* 7 */ | |||
l += p; | |||
} | |||
do { | |||
int q = 0; /* 8 */ | |||
l += q; | |||
} while (false); | |||
switch (l) { | |||
case 6: | |||
int r = 0; /* 9 */ | |||
l += r; | |||
} | |||
} | |||
int s = 0; /* 10 */ | |||
this.k += i; | |||
this.k += j; | |||
this.k += k; | |||
this.k += l; | |||
this.k += s; | |||
} | |||
} |