git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@63 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -259,6 +259,7 @@ see javassist.Dump. | |||
<li>javassist.bytecode.DeprecatedAttribute has been added. | |||
<li>javassist.bytecode.LocalVariableAttribute has been added. | |||
<li>CtClass.getURL() and javassist.ClassPath.find() has been added. | |||
<li>CtBehavior.insertAt() has been added. | |||
</ul> | |||
<p>- version 2.6 in August, 2003. |
@@ -672,4 +672,82 @@ public abstract class CtBehavior extends CtMember { | |||
throw new CannotCompileException(e); | |||
} | |||
} | |||
/** | |||
* Inserts bytecode at the specified line in the body. It is equivalent | |||
* to insertAt(lineNum, true, src). | |||
* | |||
* @param lineNum the line number. | |||
* @param src the source code representing the inserted bytecode. | |||
* It must be a single statement or block. | |||
*/ | |||
public int insertAt(int lineNum, String src) | |||
throws CannotCompileException | |||
{ | |||
return insertAt(lineNum, true, src); | |||
} | |||
/** | |||
* Inserts bytecode at the specified line in the body. | |||
* | |||
* @param lineNum the line number. | |||
* @param modify if false, this method does not insert the bytecode. | |||
* It instead only returns the line number at which | |||
* the bytecode would be inserted. | |||
* @param src the source code representing the inserted bytecode. | |||
* It must be a single statement or block. | |||
* If modify is false, the value of src can be null. | |||
*/ | |||
public int insertAt(int lineNum, boolean modify, String src) | |||
throws CannotCompileException | |||
{ | |||
CodeAttribute ca = methodInfo.getCodeAttribute(); | |||
if (ca == null) | |||
throw new CannotCompileException("no method body"); | |||
LineNumberAttribute ainfo | |||
= (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); | |||
if (ainfo == null) | |||
throw new CannotCompileException("no line number info"); | |||
LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); | |||
lineNum = pc.line; | |||
int index = pc.index; | |||
if (!modify) | |||
return lineNum; | |||
declaringClass.checkModify(); | |||
CodeIterator iterator = ca.iterator(); | |||
Javac jv = new Javac(declaringClass); | |||
try { | |||
jv.recordParams(getParameterTypes(), | |||
Modifier.isStatic(getModifiers())); | |||
jv.compileStmnt(src); | |||
Bytecode b = jv.getBytecode(); | |||
int stack = b.getMaxStack(); | |||
int locals = b.getMaxLocals(); | |||
/* 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); | |||
if (locals > ca.getMaxLocals()) | |||
ca.setMaxLocals(locals); | |||
iterator.insert(index, b.get()); | |||
iterator.insert(b.getExceptionTable(), index); | |||
return lineNum; | |||
} | |||
catch (NotFoundException e) { | |||
throw new CannotCompileException(e); | |||
} | |||
catch (CompileError e) { | |||
throw new CannotCompileException(e); | |||
} | |||
catch (BadBytecode e) { | |||
throw new CannotCompileException(e); | |||
} | |||
} | |||
} |
@@ -295,7 +295,7 @@ public class CodeAttribute extends AttributeInfo implements Opcode { | |||
LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), | |||
newCode, destCp, classnames); | |||
return LdcEntry.doit(newCode, ldc, etable); | |||
return LdcEntry.doit(newCode, ldc, etable, this); | |||
} | |||
private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, | |||
@@ -377,12 +377,13 @@ final class LdcEntry { | |||
int where; | |||
int index; | |||
static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable) | |||
static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, | |||
CodeAttribute ca) | |||
throws BadBytecode | |||
{ | |||
while (ldc != null) { | |||
int where = ldc.where; | |||
code = CodeIterator.insertGap(code, where, 1, false, etable); | |||
code = CodeIterator.insertGap(code, where, 1, false, etable, ca); | |||
code[where] = (byte)Opcode.LDC_W; | |||
ByteArray.write16bit(ldc.index, code, where + 1); | |||
ldc = ldc.next; |
@@ -450,7 +450,7 @@ public class CodeIterator implements Opcode { | |||
int cur = currentPos; | |||
byte[] c = insertGap(bytecode, pos, length, exclusive, | |||
get().getExceptionTable()); | |||
get().getExceptionTable(), codeAttr); | |||
int length2 = c.length - bytecode.length; | |||
if (cur >= pos) | |||
currentPos = cur + length2; | |||
@@ -599,19 +599,19 @@ public class CodeIterator implements Opcode { | |||
* a multiple of 4. | |||
*/ | |||
static byte[] insertGap(byte[] code, int where, int gapLength, | |||
boolean exclusive, ExceptionTable etable) | |||
boolean exclusive, ExceptionTable etable, CodeAttribute ca) | |||
throws BadBytecode | |||
{ | |||
if (gapLength <= 0) | |||
return code; | |||
try { | |||
return insertGap0(code, where, gapLength, exclusive, etable); | |||
return insertGap0(code, where, gapLength, exclusive, etable, ca); | |||
} | |||
catch (AlignmentException e) { | |||
try { | |||
return insertGap0(code, where, (gapLength + 3) & ~3, | |||
exclusive, etable); | |||
exclusive, etable, ca); | |||
} | |||
catch (AlignmentException e2) { | |||
throw new RuntimeException("fatal error?"); | |||
@@ -620,13 +620,24 @@ public class CodeIterator implements Opcode { | |||
} | |||
private static byte[] insertGap0(byte[] code, int where, int gapLength, | |||
boolean exclusive, ExceptionTable etable) | |||
boolean exclusive, ExceptionTable etable, | |||
CodeAttribute ca) | |||
throws BadBytecode, AlignmentException | |||
{ | |||
int codeLength = code.length; | |||
byte[] newcode = new byte[codeLength + gapLength]; | |||
insertGap2(code, where, gapLength, codeLength, newcode, exclusive); | |||
etable.shiftPc(where, gapLength, exclusive); | |||
LineNumberAttribute na | |||
= (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); | |||
if (na != null) | |||
na.shiftPc(where, gapLength, exclusive); | |||
LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( | |||
LocalVariableAttribute.tag); | |||
if (va != null) | |||
va.shiftPc(where, gapLength, exclusive); | |||
return newcode; | |||
} | |||
@@ -102,6 +102,53 @@ public class LineNumberAttribute extends AttributeInfo { | |||
return -1; | |||
} | |||
/** | |||
* Used as a return type of <code>toNearPc()</code>. | |||
*/ | |||
static public class Pc { | |||
/** | |||
* The index into the code array. | |||
*/ | |||
public int index; | |||
/** | |||
* The line number. | |||
*/ | |||
public int line; | |||
} | |||
/** | |||
* Returns the index into the code array at which the code for | |||
* the specified line (or the nearest line after the specified one) | |||
* begins. | |||
* | |||
* @param line the line number. | |||
* @return a pair of the index and the line number of the | |||
* bytecode at that index. | |||
*/ | |||
public Pc toNearPc(int line) { | |||
int n = tableLength(); | |||
int nearPc = 0; | |||
int distance = 0; | |||
if (n > 0) { | |||
distance = lineNumber(0) - line; | |||
nearPc = startPc(0); | |||
} | |||
for (int i = 1; i < n; ++i) { | |||
int d = lineNumber(i) - line; | |||
if ((d < 0 && d > distance) | |||
|| (d >= 0 && (d < distance || distance < 0))) { | |||
distance = d; | |||
nearPc = startPc(i); | |||
} | |||
} | |||
Pc res = new Pc(); | |||
res.index = nearPc; | |||
res.line = line + distance; | |||
return res; | |||
} | |||
/** | |||
* Makes a copy. | |||
* | |||
@@ -118,4 +165,17 @@ public class LineNumberAttribute extends AttributeInfo { | |||
LineNumberAttribute attr = new LineNumberAttribute(newCp, dest); | |||
return attr; | |||
} | |||
/** | |||
* Adjusts start_pc if bytecode is inserted in a method body. | |||
*/ | |||
void shiftPc(int where, int gapLength, boolean exclusive) { | |||
int n = tableLength(); | |||
for (int i = 0; i < n; ++i) { | |||
int pos = i * 4 + 2; | |||
int pc = ByteArray.readU16bit(info, pos); | |||
if (pc > where || (exclusive && pc == where)) | |||
ByteArray.write16bit(pc + gapLength, info, pos); | |||
} | |||
} | |||
} |
@@ -68,6 +68,22 @@ public class LocalVariableAttribute extends AttributeInfo { | |||
return ByteArray.readU16bit(info, i * 10 + 4); | |||
} | |||
/** | |||
* Adjusts start_pc and length if bytecode is inserted in a method body. | |||
*/ | |||
void shiftPc(int where, int gapLength, boolean exclusive) { | |||
int n = tableLength(); | |||
for (int i = 0; i < n; ++i) { | |||
int pos = i * 10 + 2; | |||
int pc = ByteArray.readU16bit(info, pos); | |||
int len = ByteArray.readU16bit(info, pos + 2); | |||
if (pc > where || (exclusive && pc == where)) | |||
ByteArray.write16bit(pc + gapLength, info, pos); | |||
else if (pc + len > where) | |||
ByteArray.write16bit(len + gapLength, info, pos + 2); | |||
} | |||
} | |||
/** | |||
* Returns <code>local_variable_table[i].name_index</code>. | |||
* This represents the name of the local variable. |