You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
25 KiB
830 lines
25 KiB
/* Instruction Copyright (C) 1999-2002 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; see the file COPYING.LESSER. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package jode.bytecode;
|
|
|
|
/**
|
|
* This class represents an instruction in the byte code.
|
|
*
|
|
*/
|
|
public final class Instruction implements Opcodes{
|
|
/**
|
|
* The opcode of the instruction. We map some opcodes, e.g.
|
|
* <pre>
|
|
* iload_[0-3] -> iload, ldc_w -> ldc, wide iinc -> iinc.
|
|
* </pre>
|
|
*/
|
|
// a byte would be enough, but then we would need an unsigned convert.
|
|
private int opcode;
|
|
/**
|
|
* If this opcode uses a local this gives the slot. For multianewarray
|
|
* this gives the dimension.
|
|
*/
|
|
private int shortData;
|
|
/**
|
|
* The address of this opcode.
|
|
*/
|
|
private int addr;
|
|
/**
|
|
* Optional object data for this opcode. There are four different
|
|
* usages of this field:
|
|
* <dl>
|
|
* <dt>opc_ldc / opc_ldc2_w</dt>
|
|
* <dd>The constant of type Integer/Long/Float/Double/String. </dd>
|
|
* <dt>opc_invokexxx / opc_xxxfield / opc_xxxstatic</dt>
|
|
* <dd>The field/method Reference</dd>
|
|
* <dt>opc_new / opc_checkcast / opc_instanceof / opc_multianewarray</dt>
|
|
* <dd>The typesignature of the class/array</dd>
|
|
* <dt>opc_lookupswitch</dt>
|
|
* <dd>The array of values of type int[]</dd>
|
|
* </dl>
|
|
*/
|
|
private Object objData;
|
|
/**
|
|
* The successors of this opcodes, where flow may lead to
|
|
* (except that nextByAddr is implicit if !alwaysJump). The
|
|
* value null means no successor, if there is one succesor, this
|
|
* is of type Instruction, otherwise, this is an array of Instruction.
|
|
*/
|
|
private Object succs;
|
|
/**
|
|
* The predecessors of this opcode, orthogonal to the succs array.
|
|
* This must be null or a non empty array.
|
|
*/
|
|
private Instruction[] preds;
|
|
/**
|
|
* The next instruction in code order.
|
|
*/
|
|
Instruction nextByAddr;
|
|
/**
|
|
* The previous instruction in code order, useful when changing
|
|
* the order.
|
|
*/
|
|
Instruction prevByAddr;
|
|
|
|
/**
|
|
* You can use this field to add some info to each instruction.
|
|
* After using, you must set it to null again.
|
|
* @XXX Do we really need this. Every field here can quickly take
|
|
* half a megabyte!
|
|
*/
|
|
private Object tmpInfo;
|
|
|
|
public Instruction(int opcode) {
|
|
this.opcode = opcode;
|
|
}
|
|
|
|
/**
|
|
* Returns the opcode of the instruction. We map some opcodes:
|
|
* <pre>
|
|
* [iflda]load_x -> [iflda]load
|
|
* [iflda]store_x -> [iflda]store
|
|
* [ifa]const_xx, ldc_w -> ldc
|
|
* [dl]const_xx -> ldc2_w
|
|
* wide opcode -> opcode
|
|
* tableswitch -> lookupswitch
|
|
* [a]newarray -> multianewarray
|
|
* </pre>
|
|
*/
|
|
public final int getOpcode() {
|
|
return opcode;
|
|
}
|
|
|
|
/**
|
|
* Returns the address of this opcode. As long as you don't remove
|
|
* or insert instructions, you can be sure, that the addresses of the
|
|
* opcodes are unique, and that
|
|
* <pre>
|
|
* instr.getAddr() + instr.getLength() == instr.getNextByAddr().getAddr()
|
|
* <pre>
|
|
*
|
|
* If you insert/remove Instructions, you should be aware that the
|
|
* above property is not guaranteed anymore.
|
|
*/
|
|
public final int getAddr() {
|
|
return addr;
|
|
}
|
|
|
|
public final int getNextAddr() {
|
|
return nextByAddr.addr;
|
|
}
|
|
|
|
/**
|
|
* Returns the length of this opcode. See getAddr() for some
|
|
* notes. Note that the length doesn't necessarily reflect the
|
|
* real length, when this bytecode is written again, since the
|
|
* length of an ldc instruction depends on the number of entries
|
|
* in constant pool, and the order they are allocated.
|
|
*/
|
|
public final int getLength() {
|
|
return getNextAddr() - addr;
|
|
}
|
|
|
|
final void setAddr(int addr) {
|
|
this.addr = addr;
|
|
}
|
|
|
|
public final boolean hasLocalSlot() {
|
|
return opcode == opc_iinc || opcode == opc_ret
|
|
|| opcode >= opc_iload && opcode <= opc_aload
|
|
|| opcode >= opc_istore && opcode <= opc_astore;
|
|
}
|
|
|
|
public final int getLocalSlot()
|
|
/*{ require { hasLocalSlot()
|
|
:: "Instruction has no slot" } }*/
|
|
{
|
|
return shortData;
|
|
}
|
|
|
|
public final void setLocalSlot(int slot)
|
|
/*{ require { hasLocalSlot()
|
|
:: "Instruction has no slot" } }*/
|
|
{
|
|
shortData = slot;
|
|
}
|
|
|
|
/**
|
|
* Optional integer data for this opcode. There are various uses
|
|
* for this:
|
|
* <dl>
|
|
* <dt>opc_iinc</dt>
|
|
* <dd>The value by which the constant is increased/decreased. (short)</dd>
|
|
* <dt>opc_multianewarray</dt>
|
|
* <dd>The number of dimensions (1..255)</dd>
|
|
* </dl>
|
|
*/
|
|
public final int getIncrement()
|
|
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
|
|
|| opcode == opc_tableswitch
|
|
:: "Instruction has no int data" } }*/
|
|
{
|
|
/* shortData already used for local slot */
|
|
return ((Short) objData).shortValue();
|
|
}
|
|
|
|
/**
|
|
* Optional integer data for this opcode. There are various uses
|
|
* for this:
|
|
* <dl>
|
|
* <dt>opc_iinc</dt>
|
|
* <dd>The value by which the constant is increased/decreased. (short)</dd>
|
|
* <dt>opc_multianewarray</dt>
|
|
* <dd>The number of dimensions (1..255)</dd>
|
|
* </dl>
|
|
*/
|
|
public final void setIncrement(int incr)
|
|
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
|
|
:: "Instruction has no int data" } }*/
|
|
{
|
|
/* shortData already used for local slot */
|
|
objData = new Short((short) incr);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public final int getDimensions()
|
|
/*{ require { opcode == opc_multianewarray
|
|
:: "Instruction has no dimensions" } }*/
|
|
{
|
|
return shortData;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public final void setDimensions(int dims)
|
|
/*{ require { opcode == opc_multianewarray
|
|
:: "Instruction has no dimensions" } }*/
|
|
{
|
|
shortData = dims;
|
|
}
|
|
|
|
public final Object getConstant()
|
|
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
|
|
:: "Instruction has no constant" } }*/
|
|
{
|
|
return objData;
|
|
}
|
|
|
|
public final void setConstant(Object constant)
|
|
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
|
|
:: "Instruction has no constant" } }*/
|
|
{
|
|
objData = constant;
|
|
}
|
|
|
|
public final Reference getReference()
|
|
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
|
|
:: "Instruction has no reference" } }*/
|
|
{
|
|
return (Reference) objData;
|
|
}
|
|
|
|
public final void setReference(Reference ref)
|
|
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
|
|
:: "Instruction has no reference" } }*/
|
|
{
|
|
objData = ref;
|
|
}
|
|
|
|
public final String getClazzType()
|
|
/*{ require { opcode == opc_new
|
|
|| opcode == opc_checkcast
|
|
|| opcode == opc_instanceof
|
|
|| opcode == opc_multianewarray
|
|
:: "Instruction has no typesig" } }*/
|
|
{
|
|
return (String) objData;
|
|
}
|
|
|
|
public final void setClazzType(String type)
|
|
/*{ require { opcode == opc_new
|
|
|| opcode == opc_checkcast
|
|
|| opcode == opc_instanceof
|
|
|| opcode == opc_multianewarray
|
|
:: "Instruction has no typesig" } }*/
|
|
{
|
|
objData = type;
|
|
}
|
|
|
|
public final int[] getValues()
|
|
/*{ require { opcode == opc_lookupswitch
|
|
:: "Instruction has no values" } }*/
|
|
{
|
|
return (int[]) objData;
|
|
}
|
|
|
|
public final void setValues(int[] values)
|
|
/*{ require { opcode == opc_lookupswitch
|
|
:: "Instruction has no values" } }*/
|
|
{
|
|
objData = values;
|
|
}
|
|
|
|
public final boolean doesAlwaysJump() {
|
|
switch (opcode) {
|
|
case opc_ret:
|
|
case opc_goto:
|
|
case opc_jsr:
|
|
case opc_tableswitch:
|
|
case opc_lookupswitch:
|
|
case opc_ireturn:
|
|
case opc_lreturn:
|
|
case opc_freturn:
|
|
case opc_dreturn:
|
|
case opc_areturn:
|
|
case opc_return:
|
|
case opc_athrow:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public final Instruction[] getPreds() {
|
|
return preds;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this opcode has successors, other than the implicit
|
|
* getNextByAddr().
|
|
*/
|
|
public boolean hasSuccs() {
|
|
return succs != null;
|
|
}
|
|
|
|
/**
|
|
* Returns the successors of this opcodes, where flow may lead to
|
|
* (except that nextByAddr is implicit if !alwaysJump). The
|
|
* value null means that there is no successor.
|
|
*/
|
|
public final Instruction[] getSuccs() {
|
|
if (succs instanceof Instruction)
|
|
return new Instruction[] { (Instruction) succs };
|
|
return (Instruction[]) succs;
|
|
}
|
|
|
|
/**
|
|
* Returns the single successor of this opcodes. This gives the
|
|
* target of a goto, jsr, or if opcode.
|
|
* @return null if there is no successor, otherwise the successor.
|
|
* @exception ClassCastException if this has more than one succ.
|
|
*/
|
|
public final Instruction getSingleSucc() {
|
|
return (Instruction) succs;
|
|
}
|
|
|
|
public final Instruction getPrevByAddr() {
|
|
if (prevByAddr.opcode == opc_impdep1)
|
|
return null;
|
|
return prevByAddr;
|
|
}
|
|
|
|
public final Instruction getNextByAddr() {
|
|
if (nextByAddr.opcode == opc_impdep1)
|
|
return null;
|
|
return nextByAddr;
|
|
}
|
|
|
|
public final Object getTmpInfo() {
|
|
return tmpInfo;
|
|
}
|
|
|
|
public final void setTmpInfo(Object info) {
|
|
tmpInfo = info;
|
|
}
|
|
|
|
|
|
// INTERNAL FUNCTIONS TO KEEP PREDS AND SUCCS CONSISTENT
|
|
|
|
final void removeSuccs() {
|
|
if (succs == null)
|
|
return;
|
|
if (succs instanceof Instruction[]) {
|
|
Instruction[] ss = (Instruction[]) succs;
|
|
for (int i = 0; i < ss.length; i++)
|
|
if (ss[i] != null)
|
|
ss[i].removePredecessor(this);
|
|
} else
|
|
((Instruction) succs).removePredecessor(this);
|
|
succs = null;
|
|
}
|
|
|
|
/**
|
|
* @param to may be null
|
|
*/
|
|
private final void promoteSuccs(Instruction from, Instruction to) {
|
|
if (succs == from)
|
|
succs = to;
|
|
else if (succs instanceof Instruction[]) {
|
|
Instruction[] ss = (Instruction[]) succs;
|
|
for (int i = 0; i < ss.length; i++)
|
|
if (ss[i] == from)
|
|
ss[i] = to;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @exception ClassCastException if newSuccs is neither an Instruction
|
|
* nor an array of instructions.
|
|
*/
|
|
public final void setSuccs(Object newSuccs) {
|
|
if (succs == newSuccs)
|
|
return;
|
|
removeSuccs();
|
|
if (newSuccs == null)
|
|
return;
|
|
if (newSuccs instanceof Instruction[]) {
|
|
Instruction[] ns = (Instruction[]) newSuccs;
|
|
switch (ns.length) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
succs = ns[0];
|
|
ns[0].addPredecessor(this);
|
|
break;
|
|
default:
|
|
succs = ns;
|
|
for (int i = 0; i < ns.length; i++)
|
|
ns[i].addPredecessor(this);
|
|
break;
|
|
}
|
|
} else {
|
|
succs = newSuccs;
|
|
((Instruction) newSuccs).addPredecessor(this);
|
|
}
|
|
}
|
|
|
|
void addPredecessor(Instruction pred) {
|
|
if (preds == null) {
|
|
preds = new Instruction[] { pred };
|
|
return;
|
|
}
|
|
int predsLength = preds.length;
|
|
Instruction[] newPreds = new Instruction[predsLength+1];
|
|
System.arraycopy(preds, 0, newPreds, 0, predsLength);
|
|
newPreds[predsLength] = pred;
|
|
preds = newPreds;
|
|
}
|
|
|
|
void removePredecessor(Instruction pred) {
|
|
/* Hopefully it doesn't matter if this is slow */
|
|
int predLength = preds.length;
|
|
if (predLength == 1) {
|
|
if (preds[0] != pred)
|
|
throw new jode.AssertError
|
|
("removing not existing predecessor");
|
|
preds = null;
|
|
} else {
|
|
Instruction[] newPreds = new Instruction[predLength-1];
|
|
int j;
|
|
for (j = 0; preds[j] != pred; j++)
|
|
newPreds[j] = preds[j];
|
|
System.arraycopy(preds, j+1, newPreds, j, predLength - j - 1);
|
|
preds = newPreds;
|
|
}
|
|
}
|
|
|
|
// ADDING, REMOVING AND REPLACING INSTRUCTIONS
|
|
|
|
/**
|
|
* Replaces the opcode of this instruction. You should only use the
|
|
* mapped opcodes:
|
|
* <pre>
|
|
* [iflda]load_x -> [iflda]load
|
|
* [iflda]store_x -> [iflda]store
|
|
* [ifa]const_xx, ldc_w -> ldc
|
|
* [dl]const_xx -> ldc2_w
|
|
* wide opcode -> opcode
|
|
* tableswitch -> lookupswitch
|
|
* [a]newarray -> multianewarray
|
|
* </pre>
|
|
*/
|
|
public final void replaceInstruction(Instruction newInstr,
|
|
BytecodeInfo codeinfo) {
|
|
/* remove predecessors of successors */
|
|
removeSuccs();
|
|
|
|
newInstr.addr = addr;
|
|
nextByAddr.prevByAddr = newInstr;
|
|
newInstr.nextByAddr = nextByAddr;
|
|
prevByAddr.nextByAddr = newInstr;
|
|
newInstr.prevByAddr = prevByAddr;
|
|
prevByAddr = null;
|
|
nextByAddr = null;
|
|
|
|
/* promote the successors of the predecessors to newInstr */
|
|
if (preds != null) {
|
|
for (int j=0; j < preds.length; j++)
|
|
preds[j].promoteSuccs(this, newInstr);
|
|
newInstr.preds = preds;
|
|
preds = null;
|
|
}
|
|
|
|
/* adjust exception handlers */
|
|
Handler[] handlers = codeinfo.getExceptionHandlers();
|
|
for (int i=0; i< handlers.length; i++) {
|
|
if (handlers[i].start == this)
|
|
handlers[i].start = newInstr;
|
|
if (handlers[i].end == this)
|
|
handlers[i].end = newInstr;
|
|
if (handlers[i].catcher == this)
|
|
handlers[i].catcher = newInstr;
|
|
}
|
|
|
|
/* adjust local variable table and line number table */
|
|
LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable();
|
|
if (lvt != null) {
|
|
for (int i=0; i< lvt.length; i++) {
|
|
if (lvt[i].start == this)
|
|
lvt[i].start = newInstr;
|
|
if (lvt[i].end == this)
|
|
lvt[i].end = newInstr;
|
|
}
|
|
}
|
|
LineNumber[] lnt = codeinfo.getLineNumberTable();
|
|
if (lnt != null) {
|
|
for (int i=0; i< lnt.length; i++) {
|
|
if (lnt[i].start == this)
|
|
lnt[i].start = newInstr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void appendInstruction(Instruction newInstr, BytecodeInfo codeinfo) {
|
|
newInstr.addr = nextByAddr.addr;
|
|
|
|
newInstr.nextByAddr = nextByAddr;
|
|
nextByAddr.prevByAddr = newInstr;
|
|
newInstr.prevByAddr = this;
|
|
nextByAddr = newInstr;
|
|
|
|
/* adjust exception handlers end */
|
|
Handler[] handlers = codeinfo.getExceptionHandlers();
|
|
if (handlers != null) {
|
|
for (int i=0; i< handlers.length; i++) {
|
|
if (handlers[i].end == this)
|
|
handlers[i].end = newInstr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes this instruction (as if it would be replaced by a nop).
|
|
*/
|
|
void removeInstruction(BytecodeInfo codeinfo) {
|
|
|
|
/* remove from chained list and adjust addr / length */
|
|
prevByAddr.nextByAddr = nextByAddr;
|
|
nextByAddr.prevByAddr = prevByAddr;
|
|
|
|
/* remove predecessors of successors */
|
|
removeSuccs();
|
|
|
|
/* promote the predecessors to next instruction */
|
|
if (preds != null) {
|
|
for (int j=0; j < preds.length; j++)
|
|
preds[j].promoteSuccs(this, nextByAddr);
|
|
if (nextByAddr.preds == null)
|
|
nextByAddr.preds = preds;
|
|
else {
|
|
Instruction[] newPreds = new Instruction
|
|
[nextByAddr.preds.length + preds.length];
|
|
System.arraycopy(nextByAddr.preds, 0, newPreds, 0,
|
|
nextByAddr.preds.length);
|
|
System.arraycopy(preds, 0, newPreds, nextByAddr.preds.length,
|
|
preds.length);
|
|
nextByAddr.preds = newPreds;
|
|
}
|
|
preds = null;
|
|
}
|
|
|
|
/* adjust exception handlers */
|
|
Handler[] handlers = codeinfo.getExceptionHandlers();
|
|
for (int i=0; i< handlers.length; i++) {
|
|
if (handlers[i].start == this && handlers[i].end == this) {
|
|
/* Remove the handler.
|
|
* This is very seldom, so we can make it slow */
|
|
Handler[] newHandlers = new Handler[handlers.length - 1];
|
|
System.arraycopy(handlers, 0, newHandlers, 0, i);
|
|
System.arraycopy(handlers, i+1, newHandlers, i,
|
|
handlers.length - (i+1));
|
|
handlers = newHandlers;
|
|
codeinfo.setExceptionHandlers(newHandlers);
|
|
i--;
|
|
} else {
|
|
if (handlers[i].start == this)
|
|
handlers[i].start = nextByAddr;
|
|
if (handlers[i].end == this)
|
|
handlers[i].end = prevByAddr;
|
|
if (handlers[i].catcher == this)
|
|
handlers[i].catcher = nextByAddr;
|
|
}
|
|
}
|
|
|
|
/* adjust local variable table and line number table */
|
|
LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable();
|
|
if (lvt != null) {
|
|
for (int i=0; i< lvt.length; i++) {
|
|
if (lvt[i].start == this && lvt[i].end == this) {
|
|
/* Remove the local variable info.
|
|
* This is very seldom, so we can make it slow
|
|
*/
|
|
LocalVariableInfo[] newLVT =
|
|
new LocalVariableInfo[lvt.length - 1];
|
|
System.arraycopy(lvt, 0, newLVT, 0, i);
|
|
System.arraycopy(lvt, i+1, newLVT, i,
|
|
newLVT.length - i);
|
|
lvt = newLVT;
|
|
codeinfo.setLocalVariableTable(newLVT);
|
|
i--;
|
|
} else {
|
|
if (lvt[i].start == this)
|
|
lvt[i].start = nextByAddr;
|
|
if (lvt[i].end == this)
|
|
lvt[i].end = prevByAddr;
|
|
}
|
|
}
|
|
}
|
|
LineNumber[] lnt = codeinfo.getLineNumberTable();
|
|
if (lnt != null) {
|
|
for (int i=0; i< lnt.length; i++) {
|
|
if (lnt[i].start == this) {
|
|
if (nextByAddr.opcode == opc_impdep1
|
|
|| (i+1 < lnt.length
|
|
&& lnt[i+1].start == nextByAddr)) {
|
|
/* Remove the line number.
|
|
* This is very seldom, so we can make it slow */
|
|
LineNumber[] newLNT =
|
|
new LineNumber[lnt.length - 1];
|
|
System.arraycopy(lnt, 0, newLNT, 0, i);
|
|
System.arraycopy(lnt, i+1, newLNT, i,
|
|
newLNT.length - i);
|
|
lnt = newLNT;
|
|
codeinfo.setLineNumberTable(newLNT);
|
|
i--;
|
|
} else
|
|
lnt[i].start = nextByAddr;
|
|
}
|
|
}
|
|
}
|
|
|
|
prevByAddr = null;
|
|
nextByAddr = null;
|
|
}
|
|
|
|
public int compareTo(Instruction instr) {
|
|
if (addr != instr.addr)
|
|
return addr - instr.addr;
|
|
if (this == instr)
|
|
return 0;
|
|
do {
|
|
instr = instr.nextByAddr;
|
|
if (instr.addr > addr)
|
|
return -1;
|
|
} while (instr != this);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* This returns the number of stack entries this instruction
|
|
* pushes and pops from the stack. The result fills the given
|
|
* array.
|
|
*
|
|
* @param poppush an array of two ints. The first element will
|
|
* get the number of pops, the second the number of pushes.
|
|
*/
|
|
public void getStackPopPush(int[] poppush)
|
|
/*{ require { poppush != null && poppush.length == 2
|
|
:: "poppush must be an array of two ints" } } */
|
|
{
|
|
byte delta = (byte) stackDelta.charAt(opcode);
|
|
if (delta < 0x40) {
|
|
poppush[0] = delta & 7;
|
|
poppush[1] = delta >> 3;
|
|
} else {
|
|
switch (opcode) {
|
|
case opc_invokevirtual:
|
|
case opc_invokespecial:
|
|
case opc_invokestatic:
|
|
case opc_invokeinterface: {
|
|
Reference ref = getReference();
|
|
String typeSig = ref.getType();
|
|
poppush[0] = opcode != opc_invokestatic ? 1 : 0;
|
|
poppush[0] += TypeSignature.getArgumentSize(typeSig);
|
|
poppush[1] = TypeSignature.getReturnSize(typeSig);
|
|
break;
|
|
}
|
|
|
|
case opc_putfield:
|
|
case opc_putstatic: {
|
|
Reference ref = getReference();
|
|
poppush[1] = 0;
|
|
poppush[0] = TypeSignature.getTypeSize(ref.getType());
|
|
if (opcode == opc_putfield)
|
|
poppush[0]++;
|
|
break;
|
|
}
|
|
case opc_getstatic:
|
|
case opc_getfield: {
|
|
Reference ref = getReference();
|
|
poppush[1] = TypeSignature.getTypeSize(ref.getType());
|
|
poppush[0] = opcode == opc_getfield ? 1 : 0;
|
|
break;
|
|
}
|
|
|
|
case opc_multianewarray: {
|
|
poppush[1] = 1;
|
|
poppush[0] = getDimensions();
|
|
break;
|
|
}
|
|
default:
|
|
throw new jode.AssertError("Unknown Opcode: "+opcode);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Instruction findMatchingPop() {
|
|
int poppush[] = new int[2];
|
|
getStackPopPush(poppush);
|
|
|
|
int count = poppush[1];
|
|
Instruction instr = this;
|
|
while (true) {
|
|
if (instr.succs != null || instr.doesAlwaysJump())
|
|
return null;
|
|
instr = instr.nextByAddr;
|
|
if (instr.preds != null)
|
|
return null;
|
|
|
|
instr.getStackPopPush(poppush);
|
|
if (count == poppush[0])
|
|
return instr;
|
|
count += poppush[1] - poppush[0];
|
|
}
|
|
}
|
|
|
|
public Instruction findMatchingPush() {
|
|
int count = 0;
|
|
Instruction instr = this;
|
|
int poppush[] = new int[2];
|
|
while (true) {
|
|
if (instr.preds != null)
|
|
return null;
|
|
instr = instr.prevByAddr;
|
|
if (instr == null || instr.succs != null || instr.doesAlwaysJump())
|
|
return null;
|
|
|
|
instr.getStackPopPush(poppush);
|
|
if (count < poppush[1]) {
|
|
return count == 0 ? instr : null;
|
|
}
|
|
count += poppush[0] - poppush[1];
|
|
}
|
|
}
|
|
|
|
public String getDescription() {
|
|
StringBuffer result = new StringBuffer(String.valueOf(addr))
|
|
.append('_').append(Integer.toHexString(hashCode()))
|
|
.append(": ").append(opcodeString[opcode]);
|
|
if (opcode != opc_lookupswitch) {
|
|
if (hasLocalSlot())
|
|
result.append(' ').append(getLocalSlot());
|
|
if (succs != null)
|
|
result.append(' ').append(((Instruction) succs).addr);
|
|
if (objData != null)
|
|
result.append(' ').append(objData);
|
|
if (opcode == opc_multianewarray)
|
|
result.append(' ').append(getDimensions());
|
|
} else {
|
|
int[] values = getValues();
|
|
Instruction[] succs = getSuccs();
|
|
for (int i=0; i < values.length; i++) {
|
|
result.append(' ').append(values[i]).append("->")
|
|
.append(((Instruction) succs[i]).addr);
|
|
}
|
|
result.append(' ').append("default: ")
|
|
.append(((Instruction) succs[values.length]).addr);
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
public String toString() {
|
|
return "" + addr + "_" + Integer.toHexString(hashCode());
|
|
}
|
|
|
|
private final static String stackDelta =
|
|
"\000\010\010\010\010\010\010\010\010\020\020\010\010\010\020\020\010\010\010\010\020\010\020\010\020\010\010\010\010\010\020\020\020\020\010\010\010\010\020\020\020\020\010\010\010\010\012\022\012\022\012\012\012\012\001\002\001\002\001\001\001\001\001\002\002\002\002\001\001\001\001\002\002\002\002\001\001\001\001\003\004\003\004\003\003\003\003\001\002\021\032\043\042\053\064\022\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\011\022\011\022\012\023\012\023\012\023\012\024\012\024\012\024\000\021\011\021\012\012\022\011\021\021\012\022\012\011\011\011\014\012\012\014\014\001\001\001\001\001\001\002\002\002\002\002\002\002\002\000\010\000\001\001\001\002\001\002\001\000\100\100\100\100\100\100\100\100\177\010\011\011\011\001\011\011\001\001\177\100\001\001\000\010";
|
|
|
|
/* stackDelta contains \100 if stack count of opcode is variable
|
|
* \177 if opcode is illegal, or 8*stack_push + stack_pop otherwise
|
|
* The above values are extracted from following list with:
|
|
* perl -ne'/"(.*)"/ and print $1'
|
|
*
|
|
* "\000" // nop
|
|
* "\010\010\010\010\010\010\010\010" // aconst_null, iconst_m?[0-5]
|
|
* "\020\020\010\010\010\020\020" // [lfd]const_[0-2]
|
|
* "\010\010\010\010\020" // sipush bipush ldcx
|
|
* "\010\020\010\020\010" // [ilfda]load
|
|
* "\010\010\010\010"
|
|
* "\020\020\020\020"
|
|
* "\010\010\010\010"
|
|
* "\020\020\020\020"
|
|
* "\010\010\010\010"
|
|
* "\012\022\012\022\012\012\012\012" // [ilfdabcs]aload
|
|
* "\001\002\001\002\001" // [ilfda]store
|
|
* "\001\001\001\001"
|
|
* "\002\002\002\002"
|
|
* "\001\001\001\001"
|
|
* "\002\002\002\002"
|
|
* "\001\001\001\001"
|
|
* "\003\004\003\004\003\003\003\003" // [ilfdabcs]astore
|
|
* "\001\002" // pop
|
|
* "\021\032\043\042\053\064" // dup2?(_x[12])?
|
|
* "\022" // swap
|
|
* "\012\024\012\024" // [ilfd]add
|
|
* "\012\024\012\024" // [ilfd]sub
|
|
* "\012\024\012\024" // [ilfd]mul
|
|
* "\012\024\012\024" // [ilfd]div
|
|
* "\012\024\012\024" // [ilfd]rem
|
|
* "\011\022\011\022" // [ilfd]neg
|
|
* "\012\023\012\023\012\023" // [il]u?sh[lr]
|
|
* "\012\024\012\024\012\024" // [il](and|or|xor)
|
|
* "\000" // opc_iinc
|
|
* "\021\011\021" // i2[lfd]
|
|
* "\012\012\022" // l2[ifd]
|
|
* "\011\021\021" // f2[ild]
|
|
* "\012\022\012" // d2[ilf]
|
|
* "\011\011\011" // i2[bcs]
|
|
* "\014\012\012\014\014" // [lfd]cmp.?
|
|
* "\001\001\001\001\001\001" // if..
|
|
* "\002\002\002\002\002\002" // if_icmp..
|
|
* "\002\002" // if_acmp..
|
|
* "\000\010\000\001\001" // goto,jsr,ret, .*switch
|
|
* "\001\002\001\002\001\000" // [ilfda]?return
|
|
* "\100\100\100\100" // (get/put)(static|field)
|
|
* "\100\100\100\100" // invoke.*
|
|
* "\177\010\011\011\011" // 186 - 190
|
|
* "\001\011\011\001\001" // 191 - 195
|
|
* "\177\100\001\001" // 196 - 199
|
|
* "\000\010" // goto_w, jsr_w
|
|
*/
|
|
}
|
|
|