git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@448 379699f6-c40d-0410-875b-85095c16579estable
parent
4a40e01fac
commit
1d77b7ad18
@ -0,0 +1,645 @@ |
|||||||
|
package jode.bytecode; |
||||||
|
import jode.Decompiler/*XXX*/; |
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Vector; |
||||||
|
|
||||||
|
public class BytecodeInfo extends BinaryInfo implements Opcodes { |
||||||
|
|
||||||
|
ConstantPool cp; |
||||||
|
int maxStack, maxLocals; |
||||||
|
Instruction firstInstr = null; |
||||||
|
Handler[] exceptionHandlers; |
||||||
|
|
||||||
|
private final static Object[] constants = { |
||||||
|
null, |
||||||
|
new Integer(-1), new Integer(0), new Integer(1), |
||||||
|
new Integer(2), new Integer(3), new Integer(4), new Integer(5), |
||||||
|
new Long(0), new Long(1), |
||||||
|
new Float(0), new Float(1), new Float(2), |
||||||
|
new Double(0), new Double(1) |
||||||
|
}; |
||||||
|
|
||||||
|
public void read(ConstantPool cp, |
||||||
|
DataInputStream input) throws IOException { |
||||||
|
this.cp = cp; |
||||||
|
maxStack = input.readUnsignedShort(); |
||||||
|
maxLocals = input.readUnsignedShort(); |
||||||
|
int codeLength = input.readInt(); |
||||||
|
Instruction[] instrs = new Instruction[codeLength]; |
||||||
|
int[][] succAddrs = new int[codeLength][]; |
||||||
|
{ |
||||||
|
int addr = 0; |
||||||
|
Instruction lastInstr = null; |
||||||
|
while (addr < codeLength) { |
||||||
|
Instruction instr = new Instruction(); |
||||||
|
if (lastInstr != null) { |
||||||
|
lastInstr.nextByAddr = instr; |
||||||
|
instr.prevByAddr = lastInstr; |
||||||
|
} |
||||||
|
instrs[addr] = instr; |
||||||
|
instr.addr = addr; |
||||||
|
lastInstr = instr; |
||||||
|
|
||||||
|
int opcode = input.readUnsignedByte(); |
||||||
|
if (Decompiler.isDebugging)/*XXX*/ |
||||||
|
Decompiler.err.println(addr+": "+opcodeString[opcode]); |
||||||
|
|
||||||
|
instr.opcode = opcode; |
||||||
|
switch (opcode) { |
||||||
|
case opc_wide: { |
||||||
|
int wideopcode = input.readUnsignedByte(); |
||||||
|
instr.opcode = wideopcode; |
||||||
|
switch (wideopcode) { |
||||||
|
case opc_iload: case opc_lload: |
||||||
|
case opc_fload: case opc_dload: case opc_aload: |
||||||
|
case opc_istore: case opc_lstore: |
||||||
|
case opc_fstore: case opc_dstore: case opc_astore: |
||||||
|
instr.localSlot = input.readUnsignedShort(); |
||||||
|
instr.length = 4; |
||||||
|
break; |
||||||
|
case opc_ret: |
||||||
|
instr.localSlot = input.readUnsignedShort(); |
||||||
|
instr.length = 4; |
||||||
|
instr.alwaysJumps = true; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_iinc: |
||||||
|
instr.localSlot = input.readUnsignedShort(); |
||||||
|
instr.intData = input.readShort(); |
||||||
|
instr.length = 6; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new ClassFormatError("Invalid wide opcode " |
||||||
|
+wideopcode); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case opc_iload_0: case opc_iload_1: |
||||||
|
case opc_iload_2: case opc_iload_3: |
||||||
|
case opc_lload_0: case opc_lload_1: |
||||||
|
case opc_lload_2: case opc_lload_3: |
||||||
|
case opc_fload_0: case opc_fload_1: |
||||||
|
case opc_fload_2: case opc_fload_3: |
||||||
|
case opc_dload_0: case opc_dload_1: |
||||||
|
case opc_dload_2: case opc_dload_3: |
||||||
|
case opc_aload_0: case opc_aload_1: |
||||||
|
case opc_aload_2: case opc_aload_3: |
||||||
|
instr.opcode = opc_iload + (opcode-opc_iload_0)/4; |
||||||
|
instr.localSlot = (opcode-opc_iload_0) & 3; |
||||||
|
instr.length = 1; |
||||||
|
break; |
||||||
|
case opc_istore_0: case opc_istore_1: |
||||||
|
case opc_istore_2: case opc_istore_3: |
||||||
|
case opc_lstore_0: case opc_lstore_1: |
||||||
|
case opc_lstore_2: case opc_lstore_3: |
||||||
|
case opc_fstore_0: case opc_fstore_1: |
||||||
|
case opc_fstore_2: case opc_fstore_3: |
||||||
|
case opc_dstore_0: case opc_dstore_1: |
||||||
|
case opc_dstore_2: case opc_dstore_3: |
||||||
|
case opc_astore_0: case opc_astore_1: |
||||||
|
case opc_astore_2: case opc_astore_3: |
||||||
|
instr.opcode = opc_istore + (opcode-opc_istore_0)/4; |
||||||
|
instr.localSlot = (opcode-opc_istore_0) & 3; |
||||||
|
instr.length = 1; |
||||||
|
break; |
||||||
|
case opc_iload: case opc_lload: |
||||||
|
case opc_fload: case opc_dload: case opc_aload: |
||||||
|
case opc_istore: case opc_lstore: |
||||||
|
case opc_fstore: case opc_dstore: case opc_astore: |
||||||
|
instr.localSlot = input.readUnsignedByte(); |
||||||
|
instr.length = 2; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_ret: |
||||||
|
instr.localSlot = input.readUnsignedByte(); |
||||||
|
instr.alwaysJumps = true; |
||||||
|
instr.length = 2; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_aconst_null: |
||||||
|
case opc_iconst_m1: |
||||||
|
case opc_iconst_0: case opc_iconst_1: case opc_iconst_2: |
||||||
|
case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: |
||||||
|
case opc_lconst_0: case opc_lconst_1: |
||||||
|
case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: |
||||||
|
case opc_dconst_0: case opc_dconst_1: |
||||||
|
instr.objData = constants[instr.opcode - opc_aconst_null]; |
||||||
|
instr.length = 1; |
||||||
|
break; |
||||||
|
case opc_bipush: |
||||||
|
instr.objData = new Integer(input.readByte()); |
||||||
|
instr.length = 2; |
||||||
|
break; |
||||||
|
case opc_sipush: |
||||||
|
instr.objData = new Integer(input.readShort()); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
case opc_ldc: |
||||||
|
instr.objData = cp.getConstant(input.readUnsignedByte()); |
||||||
|
instr.length = 2; |
||||||
|
break; |
||||||
|
case opc_ldc_w: |
||||||
|
instr.opcode = opc_ldc; |
||||||
|
/* fall through */ |
||||||
|
case opc_ldc2_w: |
||||||
|
instr.objData = cp.getConstant(input.readUnsignedShort()); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_iinc: |
||||||
|
instr.localSlot = input.readUnsignedByte(); |
||||||
|
instr.intData = input.readByte(); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_goto: |
||||||
|
instr.alwaysJumps = true; |
||||||
|
/* fall through */ |
||||||
|
case opc_ifeq: case opc_ifne: |
||||||
|
case opc_iflt: case opc_ifge: |
||||||
|
case opc_ifgt: case opc_ifle: |
||||||
|
case opc_if_icmpeq: case opc_if_icmpne: |
||||||
|
case opc_if_icmplt: case opc_if_icmpge: |
||||||
|
case opc_if_icmpgt: case opc_if_icmple: |
||||||
|
case opc_if_acmpeq: case opc_if_acmpne: |
||||||
|
case opc_ifnull: case opc_ifnonnull: |
||||||
|
case opc_jsr: |
||||||
|
succAddrs[addr] = new int[1]; |
||||||
|
succAddrs[addr][0] = addr+input.readShort(); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_goto_w: |
||||||
|
instr.alwaysJumps = true; |
||||||
|
/* fall through */ |
||||||
|
case opc_jsr_w: |
||||||
|
instr.opcode -= opc_goto_w - opc_goto; |
||||||
|
succAddrs[addr] = new int[1]; |
||||||
|
succAddrs[addr][0] = addr+input.readInt(); |
||||||
|
instr.length = 5; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_tableswitch: { |
||||||
|
int length = 3-(addr % 4); |
||||||
|
input.readFully(new byte[length]); |
||||||
|
int def = input.readInt(); |
||||||
|
int low = input.readInt(); |
||||||
|
int high = input.readInt(); |
||||||
|
instr.intData = low; |
||||||
|
succAddrs[addr] = new int[high-low+2]; |
||||||
|
for (int i=0; i+low <= high; i++) { |
||||||
|
succAddrs[addr][i] = addr + input.readInt(); |
||||||
|
} |
||||||
|
succAddrs[addr][high-low+1] = addr + def; |
||||||
|
instr.alwaysJumps = true; |
||||||
|
instr.length = length + 13 + 4 * (high-low+1); |
||||||
|
break; |
||||||
|
} |
||||||
|
case opc_lookupswitch: { |
||||||
|
int length = 3-(addr % 4); |
||||||
|
input.readFully(new byte[length]); |
||||||
|
int def = input.readInt(); |
||||||
|
int npairs = input.readInt(); |
||||||
|
succAddrs[addr] = new int[npairs + 1]; |
||||||
|
int[] values = new int[npairs]; |
||||||
|
for (int i=0; i < npairs; i++) { |
||||||
|
values[i] = input.readInt(); |
||||||
|
succAddrs[addr][i] = addr + input.readInt(); |
||||||
|
} |
||||||
|
succAddrs[addr][npairs] = addr + def; |
||||||
|
instr.objData = values; |
||||||
|
instr.alwaysJumps = true; |
||||||
|
instr.length = length + 9 + 8 * npairs; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case opc_ireturn: case opc_lreturn: |
||||||
|
case opc_freturn: case opc_dreturn: case opc_areturn: |
||||||
|
case opc_return: |
||||||
|
case opc_athrow: |
||||||
|
instr.alwaysJumps = true; |
||||||
|
instr.length = 1; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_getstatic: |
||||||
|
case opc_getfield: |
||||||
|
case opc_putstatic: |
||||||
|
case opc_putfield: |
||||||
|
case opc_invokespecial: |
||||||
|
case opc_invokestatic: |
||||||
|
case opc_invokevirtual: |
||||||
|
instr.objData = cp.getRef(input.readUnsignedShort()); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
case opc_invokeinterface: |
||||||
|
instr.objData = cp.getRef(input.readUnsignedShort()); |
||||||
|
instr.intData = input.readUnsignedShort(); |
||||||
|
instr.length = 5; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_new: |
||||||
|
case opc_anewarray: |
||||||
|
case opc_checkcast: |
||||||
|
case opc_instanceof: |
||||||
|
instr.objData = cp.getClassName(input.readUnsignedShort()) |
||||||
|
.replace('/','.'); |
||||||
|
instr.length = 3; |
||||||
|
break; |
||||||
|
case opc_multianewarray: |
||||||
|
instr.objData = cp.getClassName(input.readUnsignedShort()) |
||||||
|
.replace('/','.'); |
||||||
|
instr.intData = input.readUnsignedByte(); |
||||||
|
instr.length = 4; |
||||||
|
break; |
||||||
|
case opc_newarray: |
||||||
|
instr.intData = input.readUnsignedByte(); |
||||||
|
instr.length = 2; |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
if (opcode == opc_xxxunusedxxx |
||||||
|
|| opcode >= opc_breakpoint) |
||||||
|
throw new ClassFormatError("Invalid opcode "+opcode); |
||||||
|
else |
||||||
|
instr.length = 1; |
||||||
|
} |
||||||
|
addr += lastInstr.length; |
||||||
|
} |
||||||
|
} |
||||||
|
firstInstr = instrs[0]; |
||||||
|
for (Instruction instr = firstInstr; |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
int addr = instr.addr; |
||||||
|
if (succAddrs[addr] != null) { |
||||||
|
int length = succAddrs[addr].length; |
||||||
|
instr.succs = new Instruction[length]; |
||||||
|
for (int i=0; i < length; i++) { |
||||||
|
instr.succs[i] = instrs[succAddrs[addr][i]]; |
||||||
|
instr.succs[i].preds.addElement(instr); |
||||||
|
} |
||||||
|
} |
||||||
|
/* YES, if the last instruction is not reachable it may |
||||||
|
* not alwaysJump. This happens under jikes |
||||||
|
*/ |
||||||
|
if (!instr.alwaysJumps && instr.nextByAddr != null) |
||||||
|
instr.nextByAddr.preds.addElement(instr); |
||||||
|
} |
||||||
|
succAddrs = null; |
||||||
|
|
||||||
|
{ |
||||||
|
int handlersLength = input.readUnsignedShort(); |
||||||
|
exceptionHandlers = new Handler[handlersLength]; |
||||||
|
for (int i=0; i< handlersLength; i ++) { |
||||||
|
exceptionHandlers[i] = new Handler(); |
||||||
|
exceptionHandlers[i].start |
||||||
|
= instrs[input.readUnsignedShort()]; |
||||||
|
exceptionHandlers[i].end |
||||||
|
= instrs[input.readUnsignedShort()].prevByAddr; |
||||||
|
exceptionHandlers[i].catcher |
||||||
|
= instrs[input.readUnsignedShort()]; |
||||||
|
int index = input.readUnsignedShort(); |
||||||
|
exceptionHandlers[i].type = (index == 0) ? null |
||||||
|
: cp.getClassName(index).replace('/','.'); |
||||||
|
} |
||||||
|
} |
||||||
|
readAttributes(cp, input, FULLINFO); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void writeCode(GrowableConstantPool gcp, |
||||||
|
jode.obfuscator.ClassBundle bundle, |
||||||
|
DataOutputStream output) throws IOException { |
||||||
|
/* Recalculate addr and length */ |
||||||
|
int addr = 0; |
||||||
|
for (Instruction instr = firstInstr; |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
instr.addr = addr; |
||||||
|
if (instr.opcode == opc_ldc) { |
||||||
|
if (gcp.reserveConstant(instr.objData) < 256) |
||||||
|
instr.length = 2; |
||||||
|
else |
||||||
|
instr.length = 3; |
||||||
|
// if (instr.objData == null) {
|
||||||
|
// instr.length = 1;
|
||||||
|
|
||||||
|
// } else if (instr.objData instanceof Integer) {
|
||||||
|
// int value = ((Integer) instr.objData).intValue();
|
||||||
|
// if (value >= -1 && value <= 5)
|
||||||
|
// instr.length = 1;
|
||||||
|
// else if (value >= -Byte.MIN_VALUE
|
||||||
|
// && value <= Byte.MAX_VALUE)
|
||||||
|
// instr.length = 2;
|
||||||
|
// else if (value >= -Short.MIN_VALUE
|
||||||
|
// && value <= Short.MAX_VALUE)
|
||||||
|
// instr.length = 3;
|
||||||
|
// else if (gcp.reserveConstant(instr.objData) < 256)
|
||||||
|
// instr.length = 2;
|
||||||
|
// else
|
||||||
|
// instr.length = 3;
|
||||||
|
|
||||||
|
// } else if (instr.objData instanceof Long) {
|
||||||
|
// long value = ((Long) instr.objData).longValue();
|
||||||
|
// if (value == 0L || value == 1L)
|
||||||
|
// instr.length = 1;
|
||||||
|
// else
|
||||||
|
// instr.length = 3;
|
||||||
|
|
||||||
|
// } else if (instr.objData instanceof Float) {
|
||||||
|
// float value = ((Float) instr.objData).floatValue();
|
||||||
|
// if (value == 0.0F || value == 1.0F)
|
||||||
|
// instr.length = 1;
|
||||||
|
// else if (gcp.reserveConstant(instr.objData) < 256)
|
||||||
|
// instr.length = 2;
|
||||||
|
// else
|
||||||
|
// instr.length = 3;
|
||||||
|
|
||||||
|
// } else if (instr.objData instanceof Double) {
|
||||||
|
// double value = ((Double) instr.objData).doubleValue();
|
||||||
|
// if (value == 0.0 || value == 1.0)
|
||||||
|
// instr.length = 1;
|
||||||
|
// else
|
||||||
|
// instr.length = 3;
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// if (gcp.reserveConstant(instr.objData) < 256)
|
||||||
|
// instr.length = 2;
|
||||||
|
// else
|
||||||
|
// instr.length = 3;
|
||||||
|
// }
|
||||||
|
} else if (instr.localSlot != -1) { |
||||||
|
if (instr.opcode == opc_iinc) { |
||||||
|
if (instr.localSlot < 256 |
||||||
|
&& instr.intData >= Byte.MIN_VALUE |
||||||
|
&& instr.intData <= Byte.MAX_VALUE) |
||||||
|
instr.length = 3; |
||||||
|
else |
||||||
|
instr.length = 6; |
||||||
|
} else { |
||||||
|
if (instr.localSlot < 4) |
||||||
|
instr.length = 1; |
||||||
|
else if (instr.localSlot < 256) |
||||||
|
instr.length = 2; |
||||||
|
else |
||||||
|
instr.length = 4; |
||||||
|
} |
||||||
|
} else if (instr.opcode == opc_tableswitch) { |
||||||
|
int length = 3-(addr % 4); |
||||||
|
instr.length = length + 9 + 4 * instr.succs.length; |
||||||
|
} else if (instr.opcode == opc_lookupswitch) { |
||||||
|
int length = 3-(addr % 4); |
||||||
|
instr.length = length + 1 + 8 * instr.succs.length; |
||||||
|
} else if (instr.opcode == opc_goto |
||||||
|
|| instr.opcode == opc_jsr) { |
||||||
|
int dist = instr.succs[0].addr - instr.addr; |
||||||
|
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { |
||||||
|
instr.length = 5; |
||||||
|
} else |
||||||
|
instr.length = 3; |
||||||
|
} |
||||||
|
addr += instr.length; |
||||||
|
} |
||||||
|
|
||||||
|
/* Now output the code */ |
||||||
|
output.writeShort(maxStack); |
||||||
|
output.writeShort(maxLocals); |
||||||
|
output.writeInt(addr); |
||||||
|
for (Instruction instr = firstInstr; |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
switch (instr.opcode) { |
||||||
|
case opc_iload: case opc_lload: |
||||||
|
case opc_fload: case opc_dload: case opc_aload: |
||||||
|
case opc_istore: case opc_lstore: |
||||||
|
case opc_fstore: case opc_dstore: case opc_astore: |
||||||
|
if (instr.length == 1) { |
||||||
|
if (instr.opcode < opc_istore) |
||||||
|
output.writeByte(opc_iload_0 |
||||||
|
+ 4*(instr.opcode-opc_iload) |
||||||
|
+ instr.localSlot); |
||||||
|
else |
||||||
|
output.writeByte(opc_istore_0 |
||||||
|
+ 4*(instr.opcode-opc_istore) |
||||||
|
+ instr.localSlot); |
||||||
|
} else if (instr.length == 2) { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeByte(instr.localSlot); |
||||||
|
} else { |
||||||
|
output.writeByte(opc_wide); |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(instr.localSlot); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_ret: |
||||||
|
if (instr.length == 2) { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeByte(instr.localSlot); |
||||||
|
} else { |
||||||
|
output.writeByte(opc_wide); |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(instr.localSlot); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_ldc: |
||||||
|
// if (instr.objData == null)
|
||||||
|
// output.writeByte(opc_aconst_null);
|
||||||
|
|
||||||
|
// else if (instr.length == 1) {
|
||||||
|
// int value = ((Number) instr.objData).intValue();
|
||||||
|
// if (instr.objData instanceof Integer)
|
||||||
|
// output.writeByte(opc_iconst_0+value);
|
||||||
|
// else if (instr.objData instanceof Long)
|
||||||
|
// output.writeByte(opc_lconst_0+value);
|
||||||
|
// else if (instr.objData instanceof Float)
|
||||||
|
// output.writeByte(opc_fconst_0+value);
|
||||||
|
// else if (instr.objData instanceof Double)
|
||||||
|
// output.writeByte(opc_dconst_0+value);
|
||||||
|
|
||||||
|
// } else if (instr.objData instanceof Long
|
||||||
|
// || instr.objData instanceof Double) {
|
||||||
|
// output.writeByte(opc_ldc2_w);
|
||||||
|
// output.writeShort(gcp.putConstant(instr.objData));
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// if (instr.objData instanceof Integer) {
|
||||||
|
// int value = ((Integer) instr.objData).intValue();
|
||||||
|
// if (value >= -Byte.MIN_VALUE
|
||||||
|
// && value <= Byte.MAX_VALUE) {
|
||||||
|
// output.writeByte(opc_bipush);
|
||||||
|
// output.writeByte(value);
|
||||||
|
// break;
|
||||||
|
// } else if (value >= -Short.MIN_VALUE
|
||||||
|
// && value <= Short.MAX_VALUE) {
|
||||||
|
// output.writeByte(opc_sipush);
|
||||||
|
// output.writeShort(value);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (instr.length == 2) { |
||||||
|
output.writeByte(opc_ldc); |
||||||
|
output.writeByte(gcp.putConstant(instr.objData)); |
||||||
|
} else { |
||||||
|
output.writeByte(opc_ldc_w); |
||||||
|
output.writeShort(gcp.putConstant(instr.objData)); |
||||||
|
} |
||||||
|
break; |
||||||
|
case opc_ldc2_w: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(gcp.putConstant(instr.objData)); |
||||||
|
break; |
||||||
|
case opc_bipush: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeByte(((Integer)instr.objData).intValue()); |
||||||
|
break; |
||||||
|
case opc_sipush: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(((Integer)instr.objData).intValue()); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_iinc: |
||||||
|
if (instr.length == 3) { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeByte(instr.localSlot); |
||||||
|
output.writeByte(instr.intData); |
||||||
|
} else { |
||||||
|
output.writeByte(opc_wide); |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(instr.localSlot); |
||||||
|
output.writeShort(instr.intData); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_goto: |
||||||
|
case opc_ifeq: case opc_ifne: |
||||||
|
case opc_iflt: case opc_ifge: |
||||||
|
case opc_ifgt: case opc_ifle: |
||||||
|
case opc_if_icmpeq: case opc_if_icmpne: |
||||||
|
case opc_if_icmplt: case opc_if_icmpge: |
||||||
|
case opc_if_icmpgt: case opc_if_icmple: |
||||||
|
case opc_if_acmpeq: case opc_if_acmpne: |
||||||
|
case opc_ifnull: case opc_ifnonnull: |
||||||
|
case opc_jsr: |
||||||
|
if (instr.length == 3) { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(instr.succs[0].addr - instr.addr); |
||||||
|
} else { |
||||||
|
/* wide goto or jsr */ |
||||||
|
output.writeByte(instr.opcode + (opc_goto_w - opc_goto)); |
||||||
|
output.writeInt(instr.succs[0].addr - instr.addr); |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_tableswitch: { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
int align = 3-(instr.addr % 4); |
||||||
|
int numcases = instr.succs.length - 1; |
||||||
|
output.write(new byte[align]); |
||||||
|
/* def */ |
||||||
|
output.writeInt(instr.succs[numcases].addr - instr.addr); |
||||||
|
/* low */ |
||||||
|
output.writeInt(instr.intData); |
||||||
|
/* high */ |
||||||
|
output.writeInt(instr.intData + numcases - 1); |
||||||
|
for (int i=0; i < numcases; i++) |
||||||
|
output.writeInt(instr.succs[i].addr - instr.addr); |
||||||
|
break; |
||||||
|
} |
||||||
|
case opc_lookupswitch: { |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
int[] values = (int[]) instr.objData; |
||||||
|
int align = 3-(instr.addr % 4); |
||||||
|
int numcases = values.length; |
||||||
|
output.write(new byte[align]); |
||||||
|
/* def */ |
||||||
|
output.writeInt(instr.succs[numcases].addr - instr.addr); |
||||||
|
output.writeInt(numcases); |
||||||
|
for (int i=0; i < numcases; i++) { |
||||||
|
output.writeInt(values[i]); |
||||||
|
output.writeInt(instr.succs[i].addr - instr.addr); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case opc_getstatic: |
||||||
|
case opc_getfield: |
||||||
|
case opc_putstatic: |
||||||
|
case opc_putfield: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(gcp.putRef(gcp.FIELDREF, |
||||||
|
(String[]) instr.objData)); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_invokespecial: |
||||||
|
case opc_invokestatic: |
||||||
|
case opc_invokeinterface: |
||||||
|
case opc_invokevirtual: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
if (instr.opcode == opc_invokeinterface) { |
||||||
|
output.writeShort(gcp.putRef(gcp.INTERFACEMETHODREF, |
||||||
|
(String[]) instr.objData)); |
||||||
|
output.writeShort(instr.intData); |
||||||
|
} else |
||||||
|
output.writeShort(gcp.putRef(gcp.METHODREF, |
||||||
|
(String[]) instr.objData)); |
||||||
|
break; |
||||||
|
case opc_new: |
||||||
|
case opc_anewarray: |
||||||
|
case opc_checkcast: |
||||||
|
case opc_instanceof: |
||||||
|
case opc_multianewarray: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeShort(gcp.putClassRef((String) instr.objData)); |
||||||
|
if (instr.opcode == opc_multianewarray) |
||||||
|
output.writeByte(instr.intData); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_newarray: |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
output.writeByte(instr.intData); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
if (instr.opcode == opc_xxxunusedxxx |
||||||
|
|| instr.opcode >= opc_breakpoint) |
||||||
|
throw new ClassFormatError("Invalid opcode "+instr.opcode); |
||||||
|
else |
||||||
|
output.writeByte(instr.opcode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
output.writeShort(exceptionHandlers.length); |
||||||
|
for (int i=0; i< exceptionHandlers.length; i++) { |
||||||
|
output.writeShort(exceptionHandlers[i].start.addr); |
||||||
|
output.writeShort(exceptionHandlers[i].end.nextByAddr.addr); |
||||||
|
output.writeShort(exceptionHandlers[i].catcher.addr); |
||||||
|
output.writeShort((exceptionHandlers[i].type == null) ? 0 |
||||||
|
: gcp.putClassRef(exceptionHandlers[i].type)); |
||||||
|
} |
||||||
|
output.writeShort(0); // No Attributes;
|
||||||
|
} |
||||||
|
|
||||||
|
public int getMaxStack() { |
||||||
|
return maxStack; |
||||||
|
} |
||||||
|
|
||||||
|
public int getMaxLocals() { |
||||||
|
return maxLocals; |
||||||
|
} |
||||||
|
|
||||||
|
public Instruction getFirstInstr() { |
||||||
|
return firstInstr; |
||||||
|
} |
||||||
|
|
||||||
|
public Handler[] getExceptionHandlers() { |
||||||
|
return exceptionHandlers; |
||||||
|
} |
||||||
|
|
||||||
|
public void setExceptionHandlers(Handler[] handlers) { |
||||||
|
exceptionHandlers = handlers; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
package jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple class containing the info about an exception handler |
||||||
|
*/ |
||||||
|
public class Handler { |
||||||
|
public Instruction start, end, catcher; |
||||||
|
public String type; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,73 @@ |
|||||||
|
package jode.bytecode; |
||||||
|
import java.util.Vector; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
* For simplicity currently most fields are public. You shouldn't change |
||||||
|
* many of them, though. |
||||||
|
*/ |
||||||
|
public 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> |
||||||
|
*/ |
||||||
|
public int opcode; |
||||||
|
/** |
||||||
|
* If this opcode uses a local this gives the slot. This info is |
||||||
|
* used when swapping locals. |
||||||
|
*/ |
||||||
|
public int localSlot = -1; |
||||||
|
/** |
||||||
|
* Optional object data for this opcode. This is mostly used for |
||||||
|
* method/field/class references, but also for a value array |
||||||
|
* in a lookupswitch. |
||||||
|
*/ |
||||||
|
public Object objData; |
||||||
|
/** |
||||||
|
* Optional integer data for this opcode. There are various uses |
||||||
|
* for this. |
||||||
|
*/ |
||||||
|
public int intData; |
||||||
|
/** |
||||||
|
* The address of this opcode. |
||||||
|
*/ |
||||||
|
public int addr; |
||||||
|
/** |
||||||
|
* The length of this opcode. You shouldn't touch it, nor rely on |
||||||
|
* it, since the length of some opcodes may change automagically |
||||||
|
* (e.g. when changing localSlot iload_0 <-> iload 5) |
||||||
|
*/ |
||||||
|
public int length; |
||||||
|
/** |
||||||
|
* If this is true, the instruction will never flow into the nextByAddr. |
||||||
|
*/ |
||||||
|
public boolean alwaysJumps = false; |
||||||
|
/** |
||||||
|
* The successors of this opcodes, where flow may lead to |
||||||
|
* (except that nextByAddr is implicit if !alwaysJump). The |
||||||
|
* value null is equivalent to an empty array. |
||||||
|
*/ |
||||||
|
public Instruction[] succs; |
||||||
|
/** |
||||||
|
* The predecessors of this opcode, including the prevByAddr, if |
||||||
|
* that does not alwaysJump. |
||||||
|
*/ |
||||||
|
public Vector preds = new Vector(); |
||||||
|
/** |
||||||
|
* The next instruction in code order. |
||||||
|
*/ |
||||||
|
public Instruction nextByAddr; |
||||||
|
/** |
||||||
|
* The previous instruction in code order, useful when changing |
||||||
|
* the order. |
||||||
|
*/ |
||||||
|
public Instruction prevByAddr; |
||||||
|
|
||||||
|
/** |
||||||
|
* You can use this field to add some info to each instruction. |
||||||
|
*/ |
||||||
|
public Object tmpInfo; |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package jode.decompiler; |
||||||
|
import jode.bytecode.BytecodeInfo; |
||||||
|
import jode.bytecode.Instruction; |
||||||
|
import jode.bytecode.Handler; |
||||||
|
|
||||||
|
public class DeadCodeAnalysis { |
||||||
|
|
||||||
|
private final static Object reachable = new Integer(1); |
||||||
|
private final static Object reachChanged = new Integer(2); |
||||||
|
|
||||||
|
private static void propagateReachability(Instruction firstInstr, |
||||||
|
Instruction reachInstr) { |
||||||
|
if (reachInstr.tmpInfo != null) |
||||||
|
return; |
||||||
|
reachInstr.tmpInfo = reachChanged; |
||||||
|
boolean changed; |
||||||
|
do { |
||||||
|
changed = false; |
||||||
|
for (Instruction instr = firstInstr; |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
if (instr.tmpInfo == reachChanged) { |
||||||
|
changed = true; |
||||||
|
instr.tmpInfo = reachable; |
||||||
|
if (instr.succs != null) |
||||||
|
for (int i=0; i< instr.succs.length; i++) |
||||||
|
if (instr.succs[i].tmpInfo == null) |
||||||
|
instr.succs[i].tmpInfo = reachChanged; |
||||||
|
if (!instr.alwaysJumps && instr.nextByAddr != null) |
||||||
|
if (instr.nextByAddr.tmpInfo == null) |
||||||
|
instr.nextByAddr.tmpInfo = reachChanged; |
||||||
|
} |
||||||
|
} |
||||||
|
} while (changed); |
||||||
|
} |
||||||
|
|
||||||
|
public static void removeDeadCode(BytecodeInfo code) { |
||||||
|
for (Instruction instr = code.getFirstInstr(); |
||||||
|
instr != null; instr = instr.nextByAddr) |
||||||
|
instr.tmpInfo = null; |
||||||
|
|
||||||
|
propagateReachability(code.getFirstInstr(), code.getFirstInstr()); |
||||||
|
Handler[] handlers = code.getExceptionHandlers(); |
||||||
|
boolean changed; |
||||||
|
do { |
||||||
|
changed = false; |
||||||
|
for (int i=0; i < handlers.length; i++) { |
||||||
|
if (handlers[i].catcher.tmpInfo == null) { |
||||||
|
/* check if the try block is somewhere reachable |
||||||
|
* and mark the catcher as reachable then. |
||||||
|
*/ |
||||||
|
for (Instruction instr = handlers[i].start; |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
if (instr.tmpInfo != null) { |
||||||
|
propagateReachability(code.getFirstInstr(), |
||||||
|
handlers[i].catcher); |
||||||
|
changed = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
if (instr == handlers[i].end) |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} while (changed); |
||||||
|
/* Now remove the dead code */ |
||||||
|
for (Instruction instr = code.getFirstInstr(); |
||||||
|
instr != null; instr = instr.nextByAddr) { |
||||||
|
if (instr.tmpInfo == null) { |
||||||
|
/* first block is always reachable, so prevByAddr != null */ |
||||||
|
instr.prevByAddr.nextByAddr = instr.nextByAddr; |
||||||
|
instr.nextByAddr.prevByAddr = instr.prevByAddr; |
||||||
|
/* adjust length, since someon may rely on this */ |
||||||
|
instr.prevByAddr.length += instr.length; |
||||||
|
} |
||||||
|
} |
||||||
|
for (int i=0; i< handlers.length; i++) { |
||||||
|
/* A handler is not reachable iff the catcher is not reachable */ |
||||||
|
if (handlers[i].catcher.tmpInfo == null) { |
||||||
|
/* 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; |
||||||
|
code.setExceptionHandlers(newHandlers); |
||||||
|
i--; |
||||||
|
} else { |
||||||
|
/* This works! */ |
||||||
|
while (handlers[i].start.tmpInfo == null) |
||||||
|
handlers[i].start = handlers[i].start.nextByAddr; |
||||||
|
while (handlers[i].end.tmpInfo == null) |
||||||
|
handlers[i].end = handlers[i].end.prevByAddr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue