New bytecode interface:

- Cleaned up loading of class files (via ClassPath).
 - load/read can throw IOException
 - no implicit loading of information on demand (which never completely worked)
 - more documentation.
 - BasicBlock representation of method code.


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1244 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 25 years ago
parent 9c9de3b561
commit 723088e8be
  1. 960
      jode/jode/bytecode/BasicBlockReader.java
  2. 983
      jode/jode/bytecode/BasicBlockWriter.java
  3. 318
      jode/jode/bytecode/BasicBlocks.java
  4. 41
      jode/jode/bytecode/BinaryInfo.java
  5. 212
      jode/jode/bytecode/Block.java
  6. 1509
      jode/jode/bytecode/BytecodeInfo.java.in
  7. 12
      jode/jode/bytecode/ClassFormatException.java
  8. 1469
      jode/jode/bytecode/ClassInfo.java
  9. 868
      jode/jode/bytecode/ClassInfo.java.in
  10. 829
      jode/jode/bytecode/ClassPath.java
  11. 64
      jode/jode/bytecode/ConstantInstruction.java
  12. 2
      jode/jode/bytecode/ConstantPool.java
  13. 47
      jode/jode/bytecode/FieldInfo.java
  14. 4
      jode/jode/bytecode/GrowableConstantPool.java
  15. 71
      jode/jode/bytecode/Handler.java
  16. 70
      jode/jode/bytecode/IncInstruction.java
  17. 42
      jode/jode/bytecode/InnerClassInfo.java
  18. 829
      jode/jode/bytecode/Instruction.java
  19. 29
      jode/jode/bytecode/LineNumber.java
  20. 79
      jode/jode/bytecode/LocalVariableInfo.java
  21. 17
      jode/jode/bytecode/Makefile.am
  22. 121
      jode/jode/bytecode/MethodInfo.java
  23. 10
      jode/jode/bytecode/Opcodes.java
  24. 10
      jode/jode/bytecode/Reference.java
  25. 101
      jode/jode/bytecode/ReferenceInstruction.java
  26. 576
      jode/jode/bytecode/SearchPath.java
  27. 94
      jode/jode/bytecode/SlotInstruction.java
  28. 67
      jode/jode/bytecode/SwitchInstruction.java
  29. 86
      jode/jode/bytecode/TypeDimensionInstruction.java
  30. 64
      jode/jode/bytecode/TypeInstruction.java
  31. 5
      jode/jode/bytecode/TypeSignature.java

@ -0,0 +1,960 @@
/* BasicBlockReader Copyright (C) 1999-20000 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.GlobalOptions;
import java.io.DataInputStream;
import java.io.IOException;
///#def COLLECTIONS java.util
import java.util.Arrays;
import java.util.Stack;
import java.util.Vector;
import java.util.Enumeration;
///#enddef
/**
* This is a helper class, that contains the method to read in basic
* blocks from class files and write them back again.
*/
class BasicBlockReader implements Opcodes {
static final int IS_BORDER = 1;
static final int IS_REACHABLE = 2;
static final int IS_FORWARD = 4;
static final int IS_CATCHER = 8;
private class InstrInfo {
Instruction instr;
int stack;
int flags;
int addr;
int nextAddr;
int blockNr;
int[] succs;
InstrInfo () {
blockNr = stack = addr = nextAddr = -1;
}
}
private class HandlerEntry {
int start, end, catcher;
String type;
}
private class LVTEntry {
int local;
int start, end;
String name, type;
}
private class LNTEntry {
int lineNr;
int start;
}
InstrInfo[] infos;
HandlerEntry[] handlers;
BasicBlocks bb;
Block[] blocks;
public BasicBlockReader(BasicBlocks bb) {
this.bb = bb;
}
private void markReachableBlocks() throws ClassFormatException {
Stack todo = new Stack();
todo.push(infos[0]);
infos[0].flags = IS_REACHABLE;
infos[0].stack = 0;
int[] poppush = new int[2];
/* Iterate until no more reachable instructions are found */
while (!todo.isEmpty()) {
InstrInfo info = (InstrInfo) todo.pop();
int stack = info.stack;
if ((info.flags & IS_CATCHER) == 0
&& (info.instr.getOpcode() == opc_goto
|| (info.instr.getOpcode() == opc_return
&& info.stack == 0))) {
/* This is a forward block.
*/
info.flags |= IS_FORWARD;
} else {
// Check for reachable exception handlers
for (int i=0; i < handlers.length; i++) {
if (handlers[i].start <= info.addr
&& handlers[i].end > info.addr) {
InstrInfo catcher = infos[handlers[i].catcher];
if ((catcher.flags & IS_REACHABLE) == 0) {
catcher.flags |= IS_REACHABLE;
catcher.stack = 1;
todo.push(catcher);
}
}
}
InstrInfo prevInfo = null;
// Search the end of the block and calculate next stack depth
while (true) {
info.instr.getStackPopPush(poppush);
stack += poppush[1] - poppush[0];
if (stack < 0) {
throw new ClassFormatException
("Pop from empty stack: " + bb);
}
if (!info.instr.doesAlwaysJump()
&& info.succs == null
&& (infos[info.nextAddr].flags & IS_BORDER) == 0) {
prevInfo = info;
try {
info = infos[info.nextAddr];
} catch (ArrayIndexOutOfBoundsException ex) {
throw new ClassFormatException
("Flow falls out of method " + bb);
}
} else
break;
}
if (info.instr.getOpcode() == opc_goto
|| (info.instr.getOpcode() == opc_return
&& stack == 0)) {
/* If list is a goto or return, we step an instruction
* back. We don't need to modify stack, since goto and
* return are neutral.
*/
info = prevInfo;
}
}
/* mark successors as reachable */
int[] succs = info.succs;
if (succs != null) {
for (int i=0; i < succs.length; i++) {
InstrInfo succ = infos[succs[i]];
if ((succ.flags & IS_REACHABLE) == 0) {
succ.flags |= IS_REACHABLE;
int succstack = stack;
if (info.instr.getOpcode() == opc_jsr)
succstack++;
if (succ.stack < 0)
succ.stack = succstack;
else if (succ.stack != succstack)
throw new ClassFormatException
("Stack height varies: "+bb+":"+succs[i]);
todo.push(succ);
}
}
}
if (info.nextAddr < infos.length)
infos[info.nextAddr].flags |= IS_BORDER;
if (!info.instr.doesAlwaysJump()) {
InstrInfo succ = infos[info.nextAddr];
if ((succ.flags & IS_REACHABLE) == 0) {
succ.flags |= IS_REACHABLE;
if (succ.stack < 0)
succ.stack = stack;
else if (succ.stack != stack)
throw new ClassFormatException
("Stack height varies: "+bb+":"+info.nextAddr);
todo.push(succ);
}
}
}
}
private int getSuccBlockNr(int succAddr) {
InstrInfo succ = infos[succAddr];
while ((succ.flags & IS_FORWARD) != 0) {
switch (succ.instr.getOpcode()) {
case opc_goto:
succ = infos[succ.succs[0]];
break;
case opc_return:
return -1;
default:
throw new IllegalStateException();
}
}
return succ.blockNr;
}
private Block getSuccBlock(int succAddr) {
int nr = getSuccBlockNr(succAddr);
return nr == -1 ? null : blocks[nr];
}
private void convertHandlers() {
int newCount = 0;
for (int i=0; i < handlers.length; i++) {
if ((infos[handlers[i].catcher].flags & IS_REACHABLE) != 0)
newCount++;
}
Handler[] newHandlers = new Handler[newCount];
int ptr = 0;
for (int i=0; i<handlers.length; i++) {
int start = handlers[i].start;
while (infos[start].blockNr == -1)
start = infos[start].nextAddr;
int end = handlers[i].end;
while (end < infos.length
&& infos[end].blockNr == -1)
end = infos[end].nextAddr;
int endBlock = end < infos.length
? infos[end].blockNr : blocks.length;
if ((infos[handlers[i].catcher].flags & IS_REACHABLE) != 0) {
newHandlers[ptr++] = new Handler
(blocks[infos[start].blockNr],
blocks[endBlock - 1],
getSuccBlock(handlers[i].catcher),
handlers[i].type);
}
}
bb.setExceptionHandlers(newHandlers);
}
private void convertBlock(int firstAddr, int count) {
Instruction[] instrs = new Instruction[count];
InstrInfo info = infos[firstAddr];
int blockNr = info.blockNr;
instrs[0] = info.instr;
for (int i=1; i < count; i++) {
info = infos[info.nextAddr];
instrs[i] = info.instr;
}
int[] lastSuccs = info.succs;
int succLength = lastSuccs != null ? lastSuccs.length : 0;
boolean alwaysJump = info.instr.doesAlwaysJump();
Block[] succs = new Block[succLength + (alwaysJump ? 0 : 1)];
for (int i=0; i < succLength; i++)
succs[i] = getSuccBlock(lastSuccs[i]);
if (!alwaysJump)
succs[succLength] = getSuccBlock(info.nextAddr);
blocks[blockNr].setCode(Arrays.asList(instrs), succs);
}
void convert() throws ClassFormatException {
markReachableBlocks();
int blockCount = 0;
/* Count the blocks */
for (int i=0; i< infos.length; i = infos[i].nextAddr) {
if ((infos[i].flags & (IS_REACHABLE | IS_FORWARD)) == IS_REACHABLE)
infos[i].blockNr = blockCount++;
}
blocks = new Block[blockCount];
for (int i=0; i< blocks.length; i++)
blocks[i] = new Block();
int start = -1;
int count = 0;
for (int i = 0; i < infos.length; i = infos[i].nextAddr) {
InstrInfo info = infos[i];
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0) {
if ((info.flags & IS_BORDER) != 0)
GlobalOptions.err.println
(""+info.addr+": "+info.flags+","+info.blockNr+";"+info.stack);
}
if ((info.flags & IS_BORDER) != 0) {
if (start != -1)
convertBlock(start, count);
start = -1;
}
if ((info.flags & (IS_REACHABLE | IS_FORWARD)) == IS_REACHABLE) {
start = i;
count = 0;
}
if (start != -1)
count++;
}
if (start != -1)
convertBlock(start, count);
bb.setBlocks(blocks, getSuccBlock(0));
convertHandlers();
}
public void readCode(ConstantPool cp,
DataInputStream input) throws IOException {
bb.maxStack = input.readUnsignedShort();
int maxLocals = input.readUnsignedShort();
bb.maxLocals = maxLocals;
int codeLength = input.readInt();
infos = new InstrInfo[codeLength];
{
int addr = 0;
while (addr < codeLength) {
Instruction instr;
int length;
infos[addr] = new InstrInfo();
int opcode = input.readUnsignedByte();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(addr+": "+opcodeString[opcode]);
switch (opcode) {
case opc_wide: {
int wideopcode = input.readUnsignedByte();
switch (wideopcode) {
case opc_iload: case opc_fload: case opc_aload:
case opc_istore: case opc_fstore: case opc_astore: {
int slot = input.readUnsignedShort();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(wideopcode, slot);
length = 4;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print
(" " + opcodeString[wideopcode] + " " + slot);
break;
}
case opc_lload: case opc_dload:
case opc_lstore: case opc_dstore: {
int slot = input.readUnsignedShort();
if (slot >= maxLocals-1)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(wideopcode, slot);
length = 4;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print
(" " + opcodeString[wideopcode] + " " + slot);
break;
}
case opc_ret: {
int slot = input.readUnsignedShort();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(wideopcode, slot);
length = 4;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" ret "+slot);
break;
}
case opc_iinc: {
int slot = input.readUnsignedShort();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
int incr = input.readShort();
instr = new IncInstruction(wideopcode, slot, incr);
length = 6;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print
(" iinc " + slot + " " + instr.getIncrement());
break;
}
default:
throw new ClassFormatException("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: {
int slot = (opcode-opc_lload_0) & 3;
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction
(opc_iload + (opcode-opc_iload_0)/4, slot);
length = 1;
break;
}
case opc_istore_0: case opc_istore_1:
case opc_istore_2: case opc_istore_3:
case opc_fstore_0: case opc_fstore_1:
case opc_fstore_2: case opc_fstore_3:
case opc_astore_0: case opc_astore_1:
case opc_astore_2: case opc_astore_3: {
int slot = (opcode-opc_istore_0) & 3;
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction
(opc_istore + (opcode-opc_istore_0)/4, slot);
length = 1;
break;
}
case opc_lstore_0: case opc_lstore_1:
case opc_lstore_2: case opc_lstore_3:
case opc_dstore_0: case opc_dstore_1:
case opc_dstore_2: case opc_dstore_3: {
int slot = (opcode-opc_lstore_0) & 3;
if (slot >= maxLocals-1)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction
(opc_lstore + (opcode-opc_lstore_0)/4, slot);
length = 1;
break;
}
case opc_iload: case opc_fload: case opc_aload:
case opc_istore: case opc_fstore: case opc_astore: {
int slot = input.readUnsignedByte();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(opcode, slot);
length = 2;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+slot);
break;
}
case opc_lstore: case opc_dstore:
case opc_lload: case opc_dload: {
int slot = input.readUnsignedByte();
if (slot >= maxLocals - 1)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(opcode, slot);
length = 2;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+slot);
break;
}
case opc_ret: {
int slot = input.readUnsignedByte();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
instr = new SlotInstruction(opcode, slot);
length = 2;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+slot);
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_fconst_0: case opc_fconst_1: case opc_fconst_2:
instr = new ConstantInstruction
(opc_ldc, constants[opcode - opc_aconst_null]);
length = 1;
break;
case opc_lconst_0: case opc_lconst_1:
case opc_dconst_0: case opc_dconst_1:
instr = new ConstantInstruction
(opc_ldc2_w, constants[opcode - opc_aconst_null]);
length = 1;
break;
case opc_bipush:
instr = new ConstantInstruction
(opc_ldc, new Integer(input.readByte()));
length = 2;
break;
case opc_sipush:
instr = new ConstantInstruction
(opc_ldc, new Integer(input.readShort()));
length = 3;
break;
case opc_ldc: {
int index = input.readUnsignedByte();
int tag = cp.getTag(index);
if (tag != cp.STRING
&& tag != cp.INTEGER && tag != cp.FLOAT)
throw new ClassFormatException
("wrong constant tag: "+tag);
instr = new ConstantInstruction
(opc_ldc, cp.getConstant(index));
length = 2;
break;
}
case opc_ldc_w: {
int index = input.readUnsignedShort();
int tag = cp.getTag(index);
if (tag != cp.STRING
&& tag != cp.INTEGER && tag != cp.FLOAT)
throw new ClassFormatException
("wrong constant tag: "+tag);
instr = new ConstantInstruction
(opc_ldc, cp.getConstant(index));
length = 3;
break;
}
case opc_ldc2_w: {
int index = input.readUnsignedShort();
int tag = cp.getTag(index);
if (tag != cp.LONG && tag != cp.DOUBLE)
throw new ClassFormatException
("wrong constant tag: "+tag);
instr = new ConstantInstruction
(opc_ldc2_w, cp.getConstant(index));
length = 3;
break;
}
case opc_iinc: {
int slot = input.readUnsignedByte();
if (slot >= maxLocals)
throw new ClassFormatException
("Invalid local slot "+slot);
int incr = input.readByte();
instr = new IncInstruction(opcode, slot, incr);
length = 3;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print
(" " + slot + " " + instr.getIncrement());
break;
}
case opc_goto:
case opc_jsr:
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:
instr = new Instruction(opcode);
length = 3;
infos[addr].succs = new int[] { addr+input.readShort() };
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+infos[addr].succs[0]);
break;
case opc_goto_w:
case opc_jsr_w:
instr = new Instruction(opcode - (opc_goto_w - opc_goto));
length = 5;
infos[addr].succs = new int[] { addr+input.readInt() };
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+infos[addr].succs[0]);
break;
case opc_tableswitch: {
length = 3 - (addr % 4);
input.readFully(new byte[length]);
int def = input.readInt();
int low = input.readInt();
int high = input.readInt();
int[] dests = new int[high-low+1];
int npairs = 0;
for (int i=0; i < dests.length; i++) {
dests[i] = input.readInt();
if (dests[i] != def)
npairs++;
}
infos[addr].succs = new int[npairs + 1];
int[] values = new int[npairs];
int pos = 0;
for (int i=0; i < dests.length; i++) {
if (dests[i] != def) {
values[pos] = i+low;
infos[addr].succs[pos] = addr + dests[i];
pos++;
}
}
infos[addr].succs[npairs] = addr + def;
instr = new SwitchInstruction(opc_lookupswitch, values);
length += 13 + 4 * (high-low+1);
break;
}
case opc_lookupswitch: {
length = 3 - (addr % 4);
input.readFully(new byte[length]);
int def = input.readInt();
int npairs = input.readInt();
infos[addr].succs = new int[npairs + 1];
int[] values = new int[npairs];
for (int i=0; i < npairs; i++) {
values[i] = input.readInt();
if (i > 0 && values[i-1] >= values[i])
throw new ClassFormatException
("lookupswitch not sorted");
infos[addr].succs[i] = addr + input.readInt();
}
infos[addr].succs[npairs] = addr + def;
instr = new SwitchInstruction(opc_lookupswitch, values);
length += 9 + 8 * npairs;
break;
}
case opc_getstatic:
case opc_getfield:
case opc_putstatic:
case opc_putfield:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokevirtual: {
int index = input.readUnsignedShort();
int tag = cp.getTag(index);
if (opcode < opc_invokevirtual) {
if (tag != cp.FIELDREF)
throw new ClassFormatException
("field tag mismatch: "+tag);
} else {
if (tag != cp.METHODREF)
throw new ClassFormatException
("method tag mismatch: "+tag);
}
Reference ref = cp.getRef(index);
if (ref.getName().charAt(0) == '<'
&& (!ref.getName().equals("<init>")
|| opcode != opc_invokespecial))
throw new ClassFormatException
("Illegal call of special method/field "+ref);
instr = new ReferenceInstruction(opcode, ref);
length = 3;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+ref);
break;
}
case opc_invokeinterface: {
int index = input.readUnsignedShort();
int tag = cp.getTag(index);
if (tag != cp.INTERFACEMETHODREF)
throw new ClassFormatException
("interface tag mismatch: "+tag);
Reference ref = cp.getRef(index);
if (ref.getName().charAt(0) == '<')
throw new ClassFormatException
("Illegal call of special method "+ref);
int nargs = input.readUnsignedByte();
if (TypeSignature.getArgumentSize(ref.getType())
!= nargs - 1)
throw new ClassFormatException
("Interface nargs mismatch: "+ref+" vs. "+nargs);
if (input.readUnsignedByte() != 0)
throw new ClassFormatException
("Interface reserved param not zero");
instr = new ReferenceInstruction(opcode, ref);
length = 5;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+ref);
break;
}
case opc_new:
case opc_checkcast:
case opc_instanceof: {
String type = cp.getClassType(input.readUnsignedShort());
if (opcode == opc_new && type.charAt(0) == '[')
throw new ClassFormatException
("Can't create array with opc_new");
instr = new TypeInstruction(opcode, type);
length = 3;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+type);
break;
}
case opc_multianewarray: {
String type = cp.getClassType(input.readUnsignedShort());
int dims = input.readUnsignedByte();
if (dims == 0)
throw new ClassFormatException
("multianewarray dimension is 0.");
for (int i=0; i < dims; i++) {
/* Note that since type is a valid type
* signature, there must be a non bracket
* character, before the string is over.
* So there is no StringIndexOutOfBoundsException.
*/
if (type.charAt(i) != '[')
throw new ClassFormatException
("multianewarray called for non array:"+ type);
}
instr = new TypeDimensionInstruction(opcode, type, dims);
length = 4;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" " + type + " " + dims);
break;
}
case opc_anewarray: {
String type
= "["+cp.getClassType(input.readUnsignedShort());
instr = new TypeDimensionInstruction
(opc_multianewarray, type.intern(), 1);
length = 3;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+type);
break;
}
case opc_newarray: {
char sig = newArrayTypes.charAt
(input.readUnsignedByte()-4);
String type = new String (new char[] { '[', sig });
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.print(" "+type);
instr = new TypeDimensionInstruction
(opc_multianewarray, type.intern(), 1);
length = 2;
break;
}
case opc_nop:
case opc_iaload: case opc_laload: case opc_faload:
case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload:
case opc_iastore: case opc_lastore: case opc_fastore:
case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore:
case opc_pop: case opc_pop2:
case opc_dup: case opc_dup_x1: case opc_dup_x2:
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
case opc_swap:
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr:
case opc_iand: case opc_land:
case opc_ior: case opc_lor:
case opc_ixor: case opc_lxor:
case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d:
case opc_f2i: case opc_f2l: case opc_f2d:
case opc_d2i: case opc_d2l: case opc_d2f:
case opc_i2b: case opc_i2c: case opc_i2s:
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpg:
case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_return:
case opc_athrow:
case opc_arraylength:
case opc_monitorenter: case opc_monitorexit:
instr = new Instruction(opcode);
length = 1;
break;
default:
throw new ClassFormatException("Invalid opcode "+opcode);
}
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.println();
infos[addr].instr = instr;
infos[addr].addr = addr;
infos[addr].nextAddr = addr + length;
if (addr + length == codeLength && !instr.doesAlwaysJump())
throw new ClassFormatException
("Flow falls out of method " + bb);
addr += length;
}
if (addr != codeLength)
throw new ClassFormatException("last instruction too long");
}
int handlersLength = input.readUnsignedShort();
handlers = new HandlerEntry[handlersLength];
for (int i=0; i< handlersLength; i ++) {
handlers[i] = new HandlerEntry();
handlers[i].start = input.readUnsignedShort();
handlers[i].end = input.readUnsignedShort();
handlers[i].catcher = input.readUnsignedShort();
int index = input.readUnsignedShort();
handlers[i].type = (index == 0) ? null
: cp.getClassName(index);
}
for (int i=0; i< infos.length; i++) {
if (infos[i] != null && infos[i].succs != null) {
int[] succs = infos[i].succs;
for (int j=0; j < succs.length; j++) {
try {
infos[succs[j]].flags |= IS_BORDER;
} catch (RuntimeException ex) {
throw new ClassFormatException
("Illegal successor: " + bb+":"+i);
}
}
}
}
for (int i=0; i< handlersLength; i ++) {
/* Mark the instructions as border instructions.
* reachable.
*/
infos[handlers[i].start].flags |= IS_BORDER;
if (handlers[i].end < infos.length)
infos[handlers[i].end].flags |= IS_BORDER;
infos[handlers[i].catcher].flags |= IS_BORDER | IS_CATCHER;
}
}
public void readLVT(int length, ConstantPool cp,
DataInputStream input) throws IOException {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0)
GlobalOptions.err.println("LocalVariableTable of "+bb);
int count = input.readUnsignedShort();
if (length != 2 + count * 10) {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0)
GlobalOptions.err.println("Illegal LVT length, ignoring it");
return;
}
Vector[] lvt = new Vector[bb.maxLocals];
for (int i=0; i < count; i++) {
LVTEntry lve = new LVTEntry();
lve.start = input.readUnsignedShort();
lve.end = lve.start + input.readUnsignedShort();
int slot = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort();
int typeIndex = input.readUnsignedShort();
if (cp.getTag(nameIndex) != cp.UTF8
|| cp.getTag(typeIndex) != cp.UTF8) {
// This is probably an evil lvt as created by HashJava
// simply ignore it.
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_LVT) != 0)
GlobalOptions.err.println
("Illegal entry, ignoring LVT");
lvt = null;
return;
}
lve.name = cp.getUTF8(nameIndex);
lve.type = cp.getUTF8(typeIndex);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0)
GlobalOptions.err.println("\t" + lve.name + ": "
+ lve.type
+" range "+lve.start
+" - "+lve.end
+" slot "+slot);
if (lvt[slot] == null)
lvt[slot] = new Vector();
lvt[slot].addElement(lve);
}
for (int i = 0; i< infos.length; i = infos[i].nextAddr) {
Instruction instr = infos[i].instr;
if (instr.hasLocal()) {
LocalVariableInfo lvi = instr.getLocalInfo();
int slot = lvi.getSlot();
if (lvt[slot] == null)
continue;
int addr = i;
if (instr.getOpcode() >= opc_istore
&& instr.getOpcode() <= opc_astore)
addr = infos[i].nextAddr;
Enumeration enum = lvt[slot].elements();
LVTEntry match = null;
while (enum.hasMoreElements()) {
LVTEntry lve = (LVTEntry) enum.nextElement();
if (lve.start <= addr && lve.end > addr) {
if (match != null
&& (!match.name.equals(lve.name)
|| !match.type.equals(lve.type))) {
/* Multiple matches..., give no info */
match = null;
break;
}
match = lve;
}
}
if (match != null)
instr.setLocalInfo(LocalVariableInfo
.getInfo(slot, match.name, match.type));
}
}
int paramCount = bb.getParamCount();
for (int slot=0; slot< paramCount; slot++) {
if (lvt[slot] == null)
continue;
Enumeration enum = lvt[slot].elements();
LVTEntry match = null;
while (enum.hasMoreElements()) {
LVTEntry lve = (LVTEntry) enum.nextElement();
if (lve.start == 0) {
if (match != null
&& (!match.name.equals(lve.name)
|| !match.type.equals(lve.type))) {
/* Multiple matches..., give no info */
match = null;
break;
}
match = lve;
}
}
if (match != null) {
bb.setParamInfo(LocalVariableInfo
.getInfo(slot, match.name, match.type));
}
}
}
public void readLNT(int length, ConstantPool cp,
DataInputStream input) throws IOException {
int count = input.readUnsignedShort();
if (length != 2 + count * 4) {
GlobalOptions.err.println
("Illegal LineNumberTable, ignoring it");
return;
}
for (int i = 0; i < count; i++) {
int start = input.readUnsignedShort();
infos[start].instr.setLineNr(input.readUnsignedShort());
}
int lastLine = -1;
for (int i = 0; i< infos.length; i = infos[i].nextAddr) {
Instruction instr = infos[i].instr;
if (instr.hasLineNr())
lastLine = instr.getLineNr();
else
instr.setLineNr(lastLine);
}
}
}

@ -0,0 +1,983 @@
/* BasicBlockWriter Copyright (C) 1999-20000 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.BitSet;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
///#enddef
/**
* This is a helper class, that contains the method to write basic
* blocks to a class file.
*/
class BasicBlockWriter implements Opcodes {
private class LVTEntry {
int startAddr, endAddr;
Instruction start, end;
LocalVariableInfo lvi;
}
BasicBlocks bb;
int[] blockAddr;
int[][] instrLength;
int lntCount;
short[] lnt;
LVTEntry[] lvt;
boolean retAtEnd;
int lastRetAddr;
BitSet isRet;
BitSet isWide;
BitSet isWideCond;
public BasicBlockWriter(BasicBlocks bb, GrowableConstantPool gcp) {
this.bb = bb;
init(gcp);
prepare(gcp);
}
public void buildNewLVT() {
Block startBlock = bb.getStartBlock();
Block[] blocks = bb.getBlocks();
if (startBlock == null)
return;
/* We begin with the first Instruction and follow program flow.
* We remember which locals are life at start of each block
* in atStart.
*/
LocalVariableInfo[][] atStart =
new LocalVariableInfo[blocks.length][];
int startBlockNr = startBlock.getBlockNr();
atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()];
for (int i=0; i < bb.getParamCount(); i++)
atStart[startBlockNr][i] = bb.getParamInfo(i);
/* We currently ignore the jsr/ret issue. Should be okay,
* though, since it can only generate a bit too much local
* information. */
Stack todo = new Stack();
todo.push(startBlock);
while (!todo.isEmpty()) {
Block block = (Block) todo.pop();
int blockNr = block.getBlockNr();
LocalVariableInfo[] life
= (LocalVariableInfo[]) atStart[blockNr].clone();
for (Iterator iter = block.getInstructions().iterator();
iter.hasNext() ; ) {
Instruction instr = (Instruction) iter.next();
if (instr.hasLocal()) {
LocalVariableInfo lvi = instr.getLocalInfo();
int slot = lvi.getSlot();
if (life[slot] != null
&& life[slot] != lvi)
life[slot] = null;
if (life[slot] == null
&& lvi.getName() != null)
life[slot] = lvi;
}
}
Block[] succs = block.getSuccs();
if (succs != null) {
for (int j = 0; j < succs.length; j++) {
if (succs[j] == null)
continue;
int succNr = succs[j].getBlockNr();
if (atStart[succNr] == null) {
atStart[succNr] = (LocalVariableInfo[]) life.clone();
todo.push(succs[j]);
} else {
boolean changed = false;
for (int k = 0; k < life.length; k++) {
if (atStart[succNr][k] != life[k]
&& atStart[succNr][k] != null) {
atStart[succNr][k] = null;
changed = true;
}
}
if (changed && !todo.contains(succs[j]))
todo.push(succs[j]);
}
}
}
}
ArrayList lvtEntries = new ArrayList();
LVTEntry[] current = new LVTEntry[bb.getMaxLocals()];
for (int slot=0; slot < bb.getParamCount(); slot++) {
LocalVariableInfo lvi = bb.getParamInfo(slot);
if (lvi.getName() != null) {
current[slot] = new LVTEntry();
current[slot].startAddr = 0;
current[slot].lvi = lvi;
}
}
for (int i=0; i < blocks.length; i++) {
if (atStart[i] == null)
// ignore unreachable blocks:
continue;
Block block = blocks[i];
int addr = blockAddr[i];
for (int slot = 0; slot < current.length; slot++) {
if (current[slot] != null
&& current[slot].lvi != atStart[i][slot]) {
current[slot].endAddr = addr;
lvtEntries.add(current[slot]);
current[slot] = null;
}
if (current[slot] == null && atStart[i][slot] != null) {
current[slot] = new LVTEntry();
current[slot].startAddr = addr;
current[slot].lvi = atStart[i][slot];
}
}
int size = block.getInstructions().size();
for (int k = 0; k < size; k++) {
Instruction instr
= (Instruction) block.getInstructions().get(k);
if (instr.hasLocal()) {
LocalVariableInfo lvi = instr.getLocalInfo();
int slot = lvi.getSlot();
if (current[slot] != null
&& current[slot].lvi != lvi) {
current[slot].endAddr = addr;
lvtEntries.add(current[slot]);
current[slot] = null;
}
if (current[slot] == null
&& lvi.getName() != null) {
current[slot] = new LVTEntry();
current[slot].startAddr = addr;
current[slot].lvi = lvi;
}
}
addr += instrLength[i][k];
}
}
for (int slot = 0; slot < current.length; slot++) {
if (current[slot] != null) {
current[slot].endAddr = blockAddr[blockAddr.length - 1];
lvtEntries.add(current[slot]);
current[slot] = null;
}
}
if (lvtEntries.size() > 0)
lvt = (LVTEntry[]) lvtEntries.toArray
(new LVTEntry[lvtEntries.size()]);
}
public void init(GrowableConstantPool gcp) {
Block[] blocks = bb.getBlocks();
blockAddr = new int[blocks.length + 1];
instrLength = new int[blocks.length + 1][];
int[] gotos = new int[blocks.length + 1];
int[] conds = new int[blocks.length + 1];
boolean needRet = false;
boolean hasRet = false;
isRet = new BitSet();
BitSet isJsr = new BitSet();
int addr = 0;
Block startBlock = bb.getStartBlock();
if (startBlock == null) {
addr++;
isRet.set(0);
hasRet = true;
gotos[0] = -1;
} else if (startBlock != blocks[0]) {
/* reserve 3 byte for a goto at the beginning */
addr += 3;
gotos[0] = startBlock.getBlockNr();
}
next_block:
for (int i = 0; i < blocks.length; i++) {
blockAddr[i] = addr;
List instructions = blocks[i].getInstructions();
int size = instructions.size();
instrLength[i] = new int[size];
Block[] succs = blocks[i].getSuccs();
for (int j = 0; j < size; j++) {
Instruction instr = (Instruction) instructions.get(j);
if (instr.hasLineNr())
lntCount++;
conds[i+1] = -2;
int opcode = instr.getOpcode();
int length;
switch_opc:
switch (opcode) {
case opc_ldc:
case opc_ldc2_w: {
Object constant = instr.getConstant();
if (constant == null) {
length = 1;
break switch_opc;
}
for (int k = 1; k < constants.length; k++) {
if (constant.equals(constants[k])) {
length = 1;
break switch_opc;
}
}
if (opcode == opc_ldc2_w) {
gcp.putLongConstant(constant);
length = 3;
break switch_opc;
}
if (constant instanceof Integer) {
int value = ((Integer) constant).intValue();
if (value >= Byte.MIN_VALUE
&& value <= Byte.MAX_VALUE) {
length = 2;
break switch_opc;
} else if (value >= Short.MIN_VALUE
&& value <= Short.MAX_VALUE) {
length = 3;
break switch_opc;
}
}
if (gcp.putConstant(constant) < 256) {
length = 2;
} else {
length = 3;
}
break;
}
case opc_iinc: {
int slot = instr.getLocalSlot();
int increment = instr.getIncrement();
if (slot < 256
&& increment >= Byte.MIN_VALUE
&& increment <= Byte.MAX_VALUE)
length = 3;
else
length = 6;
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:
if (instr.getLocalSlot() < 4) {
length = 1;
break;
}
if (instr.getLocalSlot() < 256)
length = 2;
else
length = 4;
break;
case opc_ret: {
if (instr.getLocalSlot() < 256)
length = 2;
else
length = 4;
gotos[i+1] = -2;
break;
}
case opc_lookupswitch: {
length = 3-(addr % 4);
int[] values = instr.getValues();
int npairs = values.length;
for (int k=0; k< succs.length; k++) {
if (succs[k] == null)
needRet = true;
}
if (npairs > 0) {
int tablesize = values[npairs-1] - values[0] + 1;
if (4 + tablesize * 4 < 8 * npairs) {
// Use a table switch
length += 13 + 4 * tablesize;
break;
}
}
// Use a lookup switch
length += 9 + 8 * npairs;
// The goto is inclusive through the default part.
gotos[i+1] = -2;
continue next_block;
}
case opc_jsr:
conds[i+1] = succs[0].getBlockNr();
length = 3;
isJsr.set(i+1);
break;
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:
if (succs[0] == null) {
needRet = true;
conds[i+1] = -1;
} else
conds[i+1] = succs[0].getBlockNr();
length = 3;
break;
case opc_multianewarray: {
if (instr.getDimensions() == 1) {
String clazz = instr.getClazzType().substring(1);
if (newArrayTypes.indexOf(clazz.charAt(0)) != -1) {
length = 2;
} else {
gcp.putClassType(clazz);
length = 3;
}
} else {
gcp.putClassType(instr.getClazzType());
length = 4;
}
break;
}
case opc_getstatic:
case opc_getfield:
case opc_putstatic:
case opc_putfield:
gcp.putRef(gcp.FIELDREF, instr.getReference());
length = 3;
break;
case opc_invokespecial:
case opc_invokestatic:
case opc_invokevirtual:
gcp.putRef(gcp.METHODREF, instr.getReference());
length = 3;
break;
case opc_invokeinterface:
gcp.putRef(gcp.INTERFACEMETHODREF, instr.getReference());
length = 5;
break;
case opc_new:
case opc_checkcast:
case opc_instanceof:
gcp.putClassType(instr.getClazzType());
length = 3;
break;
case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_return:
case opc_athrow:
gotos[i+1] = -2;
length = 1;
break;
case opc_nop:
case opc_iaload: case opc_laload: case opc_faload:
case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload:
case opc_iastore: case opc_lastore: case opc_fastore:
case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore:
case opc_pop: case opc_pop2:
case opc_dup: case opc_dup_x1: case opc_dup_x2:
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
case opc_swap:
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr:
case opc_iand: case opc_land:
case opc_ior: case opc_lor:
case opc_ixor: case opc_lxor:
case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d:
case opc_f2i: case opc_f2l: case opc_f2d:
case opc_d2i: case opc_d2l: case opc_d2f:
case opc_i2b: case opc_i2c: case opc_i2s:
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpg:
case opc_arraylength:
case opc_monitorenter: case opc_monitorexit:
length = 1;
break;
default:
throw new IllegalStateException("Invalid opcode "+opcode);
}
instrLength[i][j] = length;
addr += length;
}
Block defaultSucc = succs[succs.length-1];
if (defaultSucc == null) {
// This is a return
gotos[i+1] = -1;
isRet.set(i+1);
lastRetAddr = addr;
addr++;
} else if (defaultSucc.getBlockNr() == i + 1) {
// no need for any jump
gotos[i+1] = succs[succs.length-1].getBlockNr();
} else {
// Reserve space for a normal goto.
gotos[i+1] = succs[succs.length-1].getBlockNr();
addr += 3;
}
}
if (needRet && !hasRet) {
retAtEnd = true;
lastRetAddr = addr;
addr++;
}
blockAddr[blocks.length] = addr;
isWide = new BitSet();
isWideCond = new BitSet();
// Now check for wide goto/jsr/if, but only if method is big enough
boolean changed = addr > Short.MAX_VALUE;
while (changed) {
changed = false;
for (int i = 0; i < gotos.length; i++) {
int gotoNr = gotos[i];
int condNr = conds[i];
if (!isWideCond.get(i) && condNr != -2) {
int from = blockAddr[i] - 3;
if (gotoNr != i + 1)
from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3;
int dist;
if (condNr == -1) {
if (!retAtEnd) {
dist = blockAddr[blockAddr.length-1] - 1 - from;
} else {
for (int j = 0; j < gotos.length; j++) {
if (isRet.get(j)) {
dist = blockAddr[j] - 1 - from;
if (dist >= Short.MIN_VALUE
&& dist <= Short.MAX_VALUE)
break;
}
}
throw new InternalError();
}
} else {
dist = blockAddr[condNr] - from;
}
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) {
/* We must do the a wide cond:
* if_!xxx L
* goto_w condNr
* L:goto gotoNr
*/
isWideCond.set(i);
int diff = isJsr.get(i) ? 2 : condNr == -1 ? 1 : 5;
instrLength[i][instrLength[i].length-1] += diff;
for (int j = i; j < blockAddr.length; j++)
blockAddr[j] += diff;
changed = true;
}
}
if (!isWide.get(i) && gotoNr >= 0) {
int dist = blockAddr[gotoNr] - blockAddr[i] + 3;
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) {
/* wide goto, correct addresses */
isWide.set(i);
for (int j = i; j < blockAddr.length; j++)
blockAddr[j] += 2;
changed = true;
}
}
}
}
buildNewLVT();
}
public int getSize() {
/* maxStack: 2
* maxLocals: 2
* code: 4 + codeLength
* exc count: 2
* exceptions: n * 8
* attributes:
* lvt_name: 2
* lvt_length: 4
* lvt_count: 2
* lvt_entries: n * 10
* attributes:
* lnt_name: 2
* lnt_length: 4
* lnt_count: 2
* lnt_entries: n * 4
*/
int attrsize = 0;
if (lvt != null)
attrsize += 8 + lvt.length * 10;
if (lntCount > 0)
attrsize += 8 + lntCount * 4;
return 10
+ blockAddr[blockAddr.length - 1]
+ bb.getExceptionHandlers().length * 8
+ attrsize;
}
protected int getAttributeCount() {
int count = 0;
if (lvt != null)
count++;
if (lntCount > 0)
count++;
return count;
}
public void prepare(GrowableConstantPool gcp) {
Handler[] handlers = bb.getExceptionHandlers();
for (int i = 0; i< handlers.length; i++) {
if (handlers[i].type != null)
gcp.putClassName(handlers[i].type);
}
if (lvt != null) {
gcp.putUTF8("LocalVariableTable");
int count = lvt.length;
for (int i=0; i < count; i++) {
gcp.putUTF8(lvt[i].lvi.getName());
gcp.putUTF8(lvt[i].lvi.getType());
}
}
if (lntCount > 0)
gcp.putUTF8("LineNumberTable");
}
public void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (lvt != null) {
output.writeShort(gcp.putUTF8("LocalVariableTable"));
int count = lvt.length;
int length = 2 + 10 * count;
output.writeInt(length);
output.writeShort(count);
for (int i=0; i < count; i++) {
output.writeShort(lvt[i].startAddr);
output.writeShort(lvt[i].endAddr);
output.writeShort(gcp.putUTF8(lvt[i].lvi.getName()));
output.writeShort(gcp.putUTF8(lvt[i].lvi.getType()));
output.writeShort(lvt[i].lvi.getSlot());
}
}
if (lnt != null) {
output.writeShort(gcp.putUTF8("LineNumberTable"));
int count = lnt.length / 2;
int length = 2 + 4 * count;
output.writeInt(length);
output.writeShort(count);
for (int i=0; i < count; i++) {
output.writeShort(lnt[2*i]);
output.writeShort(lnt[2*i+1]);
}
}
}
public void write(GrowableConstantPool gcp,
DataOutputStream output) throws IOException {
output.writeShort(bb.getMaxStack());
output.writeShort(bb.getMaxLocals());
Block[] blocks = bb.getBlocks();
if (blockAddr[blockAddr.length - 1] > 65535)
throw new ClassFormatError("Method too long");
output.writeInt(blockAddr[blockAddr.length-1]);
lnt = new short[lntCount*2];
int addr = 0;
Block startBlock = bb.getStartBlock();
if (isRet.get(0)) {
output.writeByte(opc_return);
addr ++;
} else if (isWide.get(0)) {
output.writeByte(opc_goto_w);
output.writeInt(blockAddr[startBlock.getBlockNr()]);
addr += 5;
} else if (startBlock != blocks[0]) {
output.writeByte(opc_goto);
output.writeShort(blockAddr[startBlock.getBlockNr()]);
addr += 3;
}
int lntPtr = 0;
next_block:
for (int i = 0; i< blocks.length; i++) {
Block[] succs = blocks[i].getSuccs();
if (addr != blockAddr[i])
throw new InternalError("Address calculation broken!");
List instructions = blocks[i].getInstructions();
int size = instructions.size();
for (int j = 0; j < size; j++) {
Instruction instr = (Instruction) instructions.get(j);
if (instr.hasLineNr()) {
lnt[lntPtr++] = (short) addr;
lnt[lntPtr++] = (short) instr.getLineNr();
}
int opcode = instr.getOpcode();
switch_opc:
switch (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: {
int slot = instr.getLocalSlot();
if (slot < 4) {
if (opcode < opc_istore)
output.writeByte(opc_iload_0
+ 4*(opcode-opc_iload)
+ slot);
else
output.writeByte(opc_istore_0
+ 4*(opcode-opc_istore)
+ slot);
} else if (slot < 256) {
output.writeByte(opcode);
output.writeByte(slot);
} else {
output.writeByte(opc_wide);
output.writeByte(opcode);
output.writeShort(slot);
}
break;
}
case opc_ret: {
int slot = instr.getLocalSlot();
if (slot < 256) {
output.writeByte(opcode);
output.writeByte(slot);
} else {
output.writeByte(opc_wide);
output.writeByte(opcode);
output.writeShort(slot);
}
continue next_block;
}
case opc_ldc:
case opc_ldc2_w: {
Object constant = instr.getConstant();
if (constant == null) {
output.writeByte(opc_aconst_null);
break switch_opc;
}
for (int k = 1; k < constants.length; k++) {
if (constant.equals(constants[k])) {
output.writeByte(opc_aconst_null + k);
break switch_opc;
}
}
if (opcode == opc_ldc2_w) {
output.writeByte(opcode);
output.writeShort(gcp.putLongConstant(constant));
} else {
if (constant instanceof Integer) {
int value = ((Integer) constant).intValue();
if (value >= Byte.MIN_VALUE
&& value <= Byte.MAX_VALUE) {
output.writeByte(opc_bipush);
output.writeByte(((Integer)constant)
.intValue());
break switch_opc;
} else if (value >= Short.MIN_VALUE
&& value <= Short.MAX_VALUE) {
output.writeByte(opc_sipush);
output.writeShort(((Integer)constant)
.intValue());
break switch_opc;
}
}
if (instrLength[i][j] == 2) {
output.writeByte(opc_ldc);
output.writeByte(gcp.putConstant(constant));
} else {
output.writeByte(opc_ldc_w);
output.writeShort(gcp.putConstant(constant));
}
}
break;
}
case opc_iinc: {
int slot = instr.getLocalSlot();
int incr = instr.getIncrement();
if (instrLength[i][j] == 3) {
output.writeByte(opcode);
output.writeByte(slot);
output.writeByte(incr);
} else {
output.writeByte(opc_wide);
output.writeByte(opcode);
output.writeShort(slot);
output.writeShort(incr);
}
break;
}
case opc_jsr: {
int dist = blockAddr[succs[0].getBlockNr()] - addr;
if (isWideCond.get(i+1)) {
/* wide jsr */
output.writeByte(opc_jsr_w);
output.writeInt(dist);
} else {
/* wide jsr */
output.writeByte(opc_jsr);
output.writeShort(dist);
}
break;
}
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: {
Block dest = succs[0];
if (isWideCond.get(i+1)) {
/* swap condition */
if (opcode >= opc_ifnull)
opcode = opcode ^ 1;
else
opcode = 1 + ((opcode - 1) ^ 1);
output.writeByte(opcode);
if (dest == null) {
output.writeShort(4);
output.writeByte(opc_ret);
} else {
output.writeShort(8);
output.writeByte(opc_goto_w);
int dist = blockAddr[dest.getBlockNr()] - addr;
output.writeInt(dist);
}
} else {
int dist;
if (dest == null) {
if (!retAtEnd) {
dist = blockAddr[blocks.length] - 1 - addr;
} else {
for (int k = 0; k < blocks.length + 1; k++) {
if (isRet.get(k)) {
dist = blockAddr[k] - 1 - addr;
if (dist >= Short.MIN_VALUE
&& dist <= Short.MAX_VALUE)
break;
}
}
throw new InternalError();
}
} else {
dist = blockAddr[dest.getBlockNr()] - addr;
}
output.writeByte(opcode);
output.writeShort(dist);
}
break;
}
case opc_lookupswitch: {
int align = 3-(addr % 4);
int[] values = instr.getValues();
int npairs = values.length;
Block defBlock = succs[npairs];
int defAddr = defBlock == null ? lastRetAddr
: blockAddr[defBlock.getBlockNr()];
if (npairs > 0) {
int tablesize = values[npairs-1] - values[0] + 1;
if (4 + tablesize * 4 < 8 * npairs) {
// Use a table switch
output.writeByte(opc_tableswitch);
output.write(new byte[align]);
/* def */
output.writeInt(defAddr - addr);
/* low */
output.writeInt(values[0]);
/* high */
output.writeInt(values[npairs-1]);
int pos = values[0];
for (int k = 0; k < npairs; k++) {
while (pos++ < values[k])
output.writeInt(defAddr - addr);
int dest = succs[k] == null ? lastRetAddr
: blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr);
}
continue next_block;
}
}
// Use a lookup switch
output.writeByte(opc_lookupswitch);
output.write(new byte[align]);
/* def */
output.writeInt(defAddr - addr);
output.writeInt(npairs);
for (int k = 0; k < npairs; k++) {
output.writeInt(values[k]);
int dest = succs[k] == null ? lastRetAddr
: blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr);
}
continue next_block;
}
case opc_getstatic:
case opc_getfield:
case opc_putstatic:
case opc_putfield:
output.writeByte(opcode);
output.writeShort(gcp.putRef(gcp.FIELDREF,
instr.getReference()));
break;
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_invokevirtual: {
Reference ref = instr.getReference();
output.writeByte(opcode);
if (opcode == opc_invokeinterface) {
output.writeShort
(gcp.putRef(gcp.INTERFACEMETHODREF, ref));
output.writeByte
(TypeSignature.getArgumentSize(ref.getType()) + 1);
output.writeByte(0);
} else
output.writeShort(gcp.putRef(gcp.METHODREF, ref));
break;
}
case opc_new:
case opc_checkcast:
case opc_instanceof:
output.writeByte(opcode);
output.writeShort(gcp.putClassType(instr.getClazzType()));
break;
case opc_multianewarray:
if (instr.getDimensions() == 1) {
String clazz = instr.getClazzType().substring(1);
int index = newArrayTypes.indexOf(clazz.charAt(0));
if (index != -1) {
output.writeByte(opc_newarray);
output.writeByte(index + 4);
} else {
output.writeByte(opc_anewarray);
output.writeShort(gcp.putClassType(clazz));
}
} else {
output.writeByte(opcode);
output.writeShort
(gcp.putClassType(instr.getClazzType()));
output.writeByte(instr.getDimensions());
}
break;
case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_athrow: case opc_return:
continue next_block;
case opc_nop:
case opc_iaload: case opc_laload: case opc_faload:
case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload:
case opc_iastore: case opc_lastore: case opc_fastore:
case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore:
case opc_pop: case opc_pop2:
case opc_dup: case opc_dup_x1: case opc_dup_x2:
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
case opc_swap:
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr:
case opc_iand: case opc_land:
case opc_ior: case opc_lor:
case opc_ixor: case opc_lxor:
case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d:
case opc_f2i: case opc_f2l: case opc_f2d:
case opc_d2i: case opc_d2l: case opc_d2f:
case opc_i2b: case opc_i2c: case opc_i2s:
case opc_lcmp: case opc_fcmpl: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpg:
case opc_arraylength:
case opc_monitorenter: case opc_monitorexit:
output.writeByte(opcode);
break;
default:
throw new ClassFormatException("Invalid opcode "+opcode);
}
addr += instrLength[i][j];
}
// Check which type of goto we should use at end of this block.
Block defaultSucc = succs[succs.length - 1];
if (isRet.get(i+1)) {
output.writeByte(opc_return);
addr++;
} else if (isWide.get(i+1)) {
output.writeByte(opc_goto_w);
output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr);
addr+=5;
} else if (defaultSucc.getBlockNr() != i+1) {
output.writeByte(opc_goto);
output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr);
addr+=3;
}
}
if (retAtEnd) {
output.writeByte(opc_return);
addr++;
}
if (addr != blockAddr[blocks.length])
throw new InternalError("Address calculation broken!");
Handler[] handlers = bb.getExceptionHandlers();
output.writeShort(handlers.length);
for (int i = 0; i< handlers.length; i++) {
output.writeShort(blockAddr[handlers[i].start.getBlockNr()]);
output.writeShort(blockAddr[handlers[i].end.getBlockNr()+1]);
output.writeShort(blockAddr[handlers[i].catcher.getBlockNr()]);
output.writeShort((handlers[i].type == null) ? 0
: gcp.putClassName(handlers[i].type));
}
}
}

@ -0,0 +1,318 @@
/* BasicBlocks Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.GlobalOptions;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
/**
* <p>This class gives another representation of the byte code of a
* method. The instructions are splitted into BasicBlocks, each an
* array of consecutive Instructions. It is not allowed that a goto
* jumps inside a basic block.</p>
*
* <p>All jumps must be at the end of the block. A conditional jump
* may be followed by a single goto, but there must not be any other
* jumps. If there is now unconditional jump at the end, the block
* implicitely flows into the next one. </p>
*
* <p>Try block must span over some consecutive BasicBlocks and there
* catches must jump to the start of an basic block. </p>
*
* <p>Deadcode will not be included in the BasicBlock, also
* BasicBlocks consisting of a single jump will be optimized away.</p>
*
* @see jode.bytecode.Instruction */
public class BasicBlocks extends BinaryInfo {
/**
* The method info which contains the basic blocks.
*/
private MethodInfo methodInfo;
/**
* The maximal number of stack entries, that may be used in this
* method.
*/
int maxStack;
/**
* The maximal number of local slots, that may be used in this
* method.
*/
int maxLocals;
/**
* This is an array of blocks, which are arrays
* of Instructions.
*/
private Block[] blocks;
/**
* The start block. Normally the first block, but differs if method start
* with a goto, e.g a while. This may be null, if this method is empty.
*/
private Block startBlock;
/**
* The local variable infos for the method parameters.
*/
private LocalVariableInfo[] paramInfos;
/**
* The array of exception handlers.
*/
private Handler[] exceptionHandlers;
public BasicBlocks(MethodInfo mi) {
methodInfo = mi;
int paramSize = (mi.isStatic() ? 0 : 1)
+ TypeSignature.getArgumentSize(mi.getType());
paramInfos = new LocalVariableInfo[paramSize];
for (int i=0; i< paramSize; i++)
paramInfos[i] = LocalVariableInfo.getInfo(i);
}
public int getMaxStack() {
return maxStack;
}
public int getMaxLocals() {
return maxLocals;
}
public MethodInfo getMethodInfo() {
return methodInfo;
}
public Block getStartBlock() {
return startBlock;
}
public Block[] getBlocks() {
return blocks;
}
public Iterator getAllInstructions() {
return new Iterator() {
int blockNr = 0;
Iterator blockIter = getNextIterator();
public boolean hasNext() {
return blockIter != null;
}
public Iterator getNextIterator() {
if (blockNr < blocks.length)
return blocks[blockNr++].getInstructions().iterator();
return null;
}
public Object next() {
Object instr;
try {
instr = blockIter.next();
} catch (NullPointerException ex) {
throw new NoSuchElementException();
}
if (!blockIter.hasNext())
blockIter = getNextIterator();
return instr;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* @return the exception handlers, or null if the method has no
* exception handlers.
*/
public Handler[] getExceptionHandlers() {
return exceptionHandlers;
}
public LocalVariableInfo getParamInfo(int i) {
return paramInfos[i];
}
public int getParamCount() {
return paramInfos.length;
}
public void setMaxStack(int ms) {
maxStack = ms;
}
public void setMaxLocals(int ml) {
maxLocals = ml;
}
public void setBlocks(Block[] blocks, Block startBlock) {
for (int i = 0; i < blocks.length; i++)
blocks[i].blockNr = i;
this.blocks = blocks;
this.startBlock = startBlock;
this.exceptionHandlers = null;
}
public void setExceptionHandlers(Handler[] handlers) {
exceptionHandlers = handlers.length == 0 ? Handler.EMPTY : handlers;
ArrayList activeHandlers = new ArrayList();
for (int i = 0; i < blocks.length; i++) {
for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getStart() == blocks[i])
activeHandlers.add(handlers[j]);
if (handlers[j].getEnd() == blocks[i])
activeHandlers.remove(handlers[j]);
}
if (activeHandlers.size() == 0)
blocks[i].catchers = Handler.EMPTY;
else
blocks[i].catchers =
(Handler[]) activeHandlers.toArray(Handler.EMPTY);
}
}
/**
* Sets the name and type of a method parameter. This overwrites
* any previously set parameter info for this slot.
* @param info a local variable info mapping a slot nr to a name
* and a type.
*/
public void setParamInfo(LocalVariableInfo info) {
paramInfos[info.getSlot()] = info;
}
private BasicBlockReader reader;
public void read(ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
GlobalOptions.err.println("Reading "+methodInfo);
reader = new BasicBlockReader(this);
reader.readCode(cp, input);
readAttributes(cp, input, howMuch);
reader.convert();
reader = null;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_BYTECODE) != 0)
dumpCode(GlobalOptions.err);
}
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL
&& name.equals("LocalVariableTable")) {
reader.readLVT(length, cp, input);
} else if (howMuch >= ClassInfo.ALMOSTALL
&& name.equals("LineNumberTable")) {
reader.readLNT(length, cp, input);
} else
super.readAttribute(name, length, cp, input, howMuch);
}
void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i=0; i < blocks.length; i++) {
next_instr:
for (Iterator iter = blocks[i].getInstructions().iterator();
iter.hasNext(); ) {
Instruction instr = (Instruction) iter.next();
if (instr.getOpcode() == Opcodes.opc_ldc) {
Object constant = instr.getConstant();
if (constant == null)
continue next_instr;
for (int j=1; j < Opcodes.constants.length; j++) {
if (constant.equals(Opcodes.constants[j]))
continue next_instr;
}
if (constant instanceof Integer) {
int value = ((Integer) constant).intValue();
if (value >= Short.MIN_VALUE
&& value <= Short.MAX_VALUE)
continue next_instr;
}
gcp.reserveConstant(constant);
}
}
}
}
BasicBlockWriter bbw;
void prepareWriting(GrowableConstantPool gcp) {
bbw = new BasicBlockWriter(this, gcp);
}
int getKnownAttributeCount() {
return bbw.getAttributeCount();
}
void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
bbw.writeAttributes(gcp, output);
}
void write(GrowableConstantPool gcp,
DataOutputStream output) throws IOException {
output.writeInt(bbw.getSize() + getAttributeSize());
bbw.write(gcp, output);
writeAttributes(gcp, output);
bbw = null;
}
public void dumpCode(PrintWriter output) {
output.println(methodInfo.getName()+methodInfo.getType()+":");
if (startBlock == null)
output.println("\treturn");
else if (startBlock != blocks[0])
output.println("\tgoto "+startBlock);
for (int i=0; i< blocks.length; i++) {
blocks[i].dumpCode(output);
}
for (int i=0; i< exceptionHandlers.length; i++) {
output.println("catch " + exceptionHandlers[i].type
+ " from " + exceptionHandlers[i].start
+ " to " + exceptionHandlers[i].end
+ " catcher " + exceptionHandlers[i].catcher);
}
}
public String toString() {
return "BasicBlocks["+methodInfo+"]";
}
}

@ -26,31 +26,21 @@ import java.io.IOException;
import java.io.InputStream;
import jode.util.SimpleMap;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.Collections;
import @COLLECTIONS@.Iterator;
///#def COLLECTIONS java.util
import java.util.Map;
import java.util.Collections;
import java.util.Iterator;
///#enddef
/**
*
* @author Jochen Hoenicke
*/
public class BinaryInfo {
public static final int HIERARCHY = 0x01;
public static final int FIELDS = 0x02;
public static final int METHODS = 0x04;
public static final int CONSTANTS = 0x08;
public static final int KNOWNATTRIBS = 0x10;
public static final int INNERCLASSES = 0x20;
public static final int OUTERCLASSES = 0x40;
public static final int UNKNOWNATTRIBS = 0x80;
public static final int FULLINFO = 0xff;
public static final int MOSTINFO = 0x7f;
public static final int REFLECTINFO = 0x6f;
class BinaryInfo {
private Map unknownAttributes = null;
protected void skipAttributes(DataInputStream input) throws IOException {
void skipAttributes(DataInputStream input) throws IOException {
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readUnsignedShort(); // the name index
@ -64,17 +54,17 @@ public class BinaryInfo {
}
}
protected int getKnownAttributeCount() {
int getKnownAttributeCount() {
return 0;
}
protected void readAttribute(String name, int length,
void readAttribute(String name, int length,
ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
byte[] data = new byte[length];
input.readFully(data);
if ((howMuch & UNKNOWNATTRIBS) != 0) {
if (howMuch >= ClassInfo.ALL) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, data);
@ -132,7 +122,7 @@ public class BinaryInfo {
}
}
protected void readAttributes(ConstantPool constantPool,
void readAttributes(ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
int count = input.readUnsignedShort();
@ -150,12 +140,11 @@ public class BinaryInfo {
}
}
public void dropInfo(int howMuch) {
if ((howMuch & UNKNOWNATTRIBS) != 0)
public void dropAttributes() {
unknownAttributes = null;
}
protected void prepareAttributes(GrowableConstantPool gcp) {
void prepareAttributes(GrowableConstantPool gcp) {
if (unknownAttributes == null)
return;
Iterator i = unknownAttributes.keySet().iterator();
@ -163,12 +152,12 @@ public class BinaryInfo {
gcp.putUTF8((String) i.next());
}
protected void writeKnownAttributes
void writeKnownAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
}
protected void writeAttributes
void writeAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
int count = getKnownAttributeCount();

@ -0,0 +1,212 @@
/* Block Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.PrintWriter;
///#def COLLECTIONS java.util
import java.util.Collection;
import java.util.Arrays;
import java.util.List;
import java.util.Iterator;
///#enddef
/**
* Represents a single basic block. It contains a list of
* instructions, the successor blocks and the exception handlers for
* this block. The last Instruction, and only the last, may be a
* conditional jump, a tableswitch or a jsr.
*
* @author Jochen Hoenicke */
public final class Block {
/**
* The opcodes of the instructions in this block.
*/
private Instruction[] instrs;
/**
* The blockNr of successor blocks
*/
private Block[] succs;
/**
* The catching blocks. Set by BasicBlocks.
*/
Handler[] catchers;
/**
* The blockNr of this block. Set by BasicBlocks.
*/
int blockNr;
/**
* Creates a new empty block, with a null successor array.
*/
public Block() {
instrs = new Instruction[0];
succs = null;
}
/**
* Gets the list of instructions. The returned list should not be
* modified, except that the instructions (but not their opcodes)
* may be modified.
*/
public List getInstructions() {
return Arrays.asList(instrs);
}
/**
* Gets the successor array. The last successor is the next basic
* block that is jumped to via goto or the default part of a
* switch. For conditional jumps and jsrs the second successor gives
* the destination.
*/
public Block[] getSuccs() {
return succs;
}
/**
* Gets the exception handlers which try part contains this block.
* You can't set them since they are calculated automatically.
* @return the exception handlers.
* @see BasicBlocks#setExceptionHandlers
*/
public Handler[] getCatchers() {
return catchers;
}
/**
* Gets the block number. The block numbers are consecutive number
* from 0 to the number of blocks in a method. The equation
* <pre> BasicBlocks.getBlock()[i].getBlockNr() == i </pre>
* always holds (as long as you don't do something dirty, like adding
* the same block to different BasicBlocks, or to the same but more
* than once).
* @return the block number.
*/
public int getBlockNr() {
return blockNr;
}
private void checkConsistent() {
/* Check if all instructions are of correct type */
int size = instrs.length;
for (int i = 0; i < size; i++) {
int opcode = instrs[i].getOpcode();
switch (opcode) {
case Opcodes.opc_goto:
throw new IllegalArgumentException("goto in block");
case Opcodes.opc_lookupswitch:
if (succs == null || succs.length == 0)
throw new IllegalArgumentException
("no successors for switch");
if (i != size - 1)
throw new IllegalArgumentException
("switch in the middle!");
return;
case Opcodes.opc_ret: case Opcodes.opc_athrow:
case Opcodes.opc_ireturn: case Opcodes.opc_lreturn:
case Opcodes.opc_freturn: case Opcodes.opc_dreturn:
case Opcodes.opc_areturn: case Opcodes.opc_return:
if (succs == null || succs.length > 0)
throw new IllegalArgumentException
("throw or return with successor.");
if (i != size - 1)
throw new IllegalArgumentException
("return in the middle!");
return;
case Opcodes.opc_ifeq: case Opcodes.opc_ifne:
case Opcodes.opc_iflt: case Opcodes.opc_ifge:
case Opcodes.opc_ifgt: case Opcodes.opc_ifle:
case Opcodes.opc_if_icmpeq: case Opcodes.opc_if_icmpne:
case Opcodes.opc_if_icmplt: case Opcodes.opc_if_icmpge:
case Opcodes.opc_if_icmpgt: case Opcodes.opc_if_icmple:
case Opcodes.opc_if_acmpeq: case Opcodes.opc_if_acmpne:
case Opcodes.opc_ifnull: case Opcodes.opc_ifnonnull:
case Opcodes.opc_jsr:
if (succs == null || succs.length != 2)
throw new IllegalArgumentException
("successors inappropriate for if/jsr");
if (i != size - 1)
throw new IllegalArgumentException
("if/jsr in the middle!");
return;
}
}
if (succs == null || succs.length != 1)
throw new IllegalArgumentException("no single successor block");
}
/**
* Set the code, i.e. instructions and successor blocks.
* The instructions must be valid and match the successors.
*/
public void setCode(Collection instrs, Block[] succs) {
this.instrs = (Instruction[])
instrs.toArray(new Instruction[instrs.size()]);
this.succs = succs;
try {
checkConsistent();
} catch (IllegalArgumentException ex) {
dumpCode(jode.GlobalOptions.err);
throw ex;
}
}
public void dumpCode(PrintWriter output) {
output.println(" "+this+":");
for (int i = 0; i < instrs.length; i++) {
Instruction instr = instrs[i];
if (i == instrs.length - 1 && succs != null) {
int opcode = instr.getOpcode();
if (opcode == Opcodes.opc_lookupswitch) {
// Special case for switch:
output.println("\tswitch");
int[] values = instr.getValues();
for (int j = 0; j < values.length; j++)
output.println("\t case"+values[j]
+": goto "+succs[j]);
output.println("\t default: goto"+
succs[values.length]);
return;
} else if (succs.length > 1) {
output.println("\t"+instr.getDescription()
+" "+succs[0]);
break;
}
}
output.println("\t"+instr.getDescription());
}
if (succs != null && succs.length > 0) {
if (succs[succs.length-1] == null)
output.println("\treturn");
else
output.println("\tgoto "+succs[succs.length-1]);
}
}
public String toString() {
return "Block_"+blockNr;
}
}

File diff suppressed because it is too large Load Diff

@ -20,13 +20,23 @@
package jode.bytecode;
/**
* This exception is thrown, if the class file has an unknown format.
* Thrown when a class file with an unknown or illegal format is loaded.
*
* @author Jochen Hoenicke
*/
public class ClassFormatException extends java.io.IOException{
/**
* Constructs a new class format exception with the given detail
* message.
* @param detail the detail message.
*/
public ClassFormatException(String detail) {
super(detail);
}
/**
* Constructs a new class format exception.
*/
public ClassFormatException() {
super();
}

File diff suppressed because it is too large Load Diff

@ -1,868 +0,0 @@
/* ClassInfo Copyright (C) 1998-1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.GlobalOptions;
import jode.util.UnifyHash;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import @COLLECTIONS@.Iterator;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* This class does represent a class similar to java.lang.Class. You
* can get the super class and the interfaces. <br>
*
* The main difference to java.lang.Class is, that the objects are builded
* from a stream containing the .class file, and that it uses the
* <code>Type</code> to represent types instead of Class itself. <br>
*
* <h2>The InnerClasses attribute</h2>
*
* The InnerClasses attribute is transformed in a special way by this
* class so we want to taker a closer look. According to the <a
* href="http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814">inner
* class specification</a> there must be an InnerClass attribute for
* every non top-level class that is referenced somewhere in the
* bytecode. This implies that if this is an inner class, it must
* contain a inner class attribute for itself. Before a class is
* referenced as outer class in an InnerClass attribute, it must be
* described by another InnerClass attribute. <br>
*
* Since every class references itself, there must be informations
* about the outer class for each class scoped class. If that outer
* class is an outer class again, there must be information about it,
* too. This particular chain of InnerClassInfos is returned by the
* getOuterClasses() method; for convenience in reverse order, i.e.
* current class first, then the outer classes from innermost to
* outermost. <br>
*
* A valid bytecode must also contain InnerClass infos for each inner
* classes it declares. These information are returned by the
* getInnerClasses() method. The order of these classes is the same
* as in the bytecode attribute.
*
* All remaining attributes are returned by getExtraClasses() in the
* same order as in the bytecode attribute.
*
* @author Jochen Hoenicke
*/
public class ClassInfo extends BinaryInfo {
private static SearchPath classpath;
private static final UnifyHash classes = new UnifyHash();
private int status = 0;
private boolean modified = false;
private int modifiers = -1;
private String name;
private ClassInfo superclass;
private ClassInfo[] interfaces;
private FieldInfo[] fields;
private MethodInfo[] methods;
private InnerClassInfo[] outerClasses;
private InnerClassInfo[] innerClasses;
private InnerClassInfo[] extraClasses;
private String sourceFile;
public final static ClassInfo javaLangObject = forName("java.lang.Object");
public static void setClassPath(String path) {
setClassPath(new SearchPath(path));
}
public static void setClassPath(SearchPath path) {
if (classpath != path) {
classpath = path;
Iterator i = classes.iterator();
while (i.hasNext()) {
ClassInfo ci = (ClassInfo) i.next();
ci.status = 0;
ci.superclass = null;
ci.fields = null;
ci.interfaces = null;
ci.methods = null;
ci.removeAllAttributes();
}
}
}
public static boolean exists(String name) {
return classpath.exists(name.replace('.', '/') + ".class");
}
public static boolean isPackage(String name) {
return classpath.isDirectory(name.replace('.', '/'));
}
public static Enumeration getClassesAndPackages(final String packageName) {
final Enumeration enum =
classpath.listFiles(packageName.replace('.','/'));
return new Enumeration() {
public boolean hasMoreElements() {
return enum.hasMoreElements();
}
public Object nextElement() {
String name = (String) enum.nextElement();
if (!name.endsWith(".class"))
// This is a package
return name;
return name.substring(0, name.length()-6);
}
};
}
public static ClassInfo forName(String name) {
if (name == null
|| name.indexOf(';') != -1
|| name.indexOf('[') != -1
|| name.indexOf('/') != -1)
throw new IllegalArgumentException("Illegal class name: "+name);
int hash = name.hashCode();
Iterator iter = classes.iterateHashCode(hash);
while (iter.hasNext()) {
ClassInfo clazz = (ClassInfo) iter.next();
if (clazz.name.equals(name))
return clazz;
}
ClassInfo clazz = new ClassInfo(name);
classes.put(hash, clazz);
return clazz;
}
private ClassInfo(String name) {
this.name = name;
}
protected void readAttribute(String name, int length,
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("SourceFile")) {
if (length != 2)
throw new ClassFormatException("SourceFile attribute"
+ " has wrong length");
sourceFile = cp.getUTF8(input.readUnsignedShort());
} else if ((howMuch & (OUTERCLASSES | INNERCLASSES)) != 0
&& name.equals("InnerClasses")) {
int count = input.readUnsignedShort();
if (length != 2 + 8 * count)
throw new ClassFormatException
("InnerClasses attribute has wrong length");
int innerCount = 0, outerCount = 0, extraCount = 0;
InnerClassInfo[] innerClassInfo = new InnerClassInfo[count];
for (int i=0; i< count; i++) {
int innerIndex = input.readUnsignedShort();
int outerIndex = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort();
String inner = cp.getClassName(innerIndex);
String outer =
outerIndex != 0 ? cp.getClassName(outerIndex) : null;
String innername =
nameIndex != 0 ? cp.getUTF8(nameIndex) : null;
int access = input.readUnsignedShort();
if (innername != null && innername.length() == 0)
innername = null;
InnerClassInfo ici = new InnerClassInfo
(inner, outer, innername, access);
if (outer != null && outer.equals(getName())
&& innername != null)
innerClassInfo[innerCount++] = ici;
else
innerClassInfo[count - (++extraCount)] = ici;
}
/* Now innerClasses are at the front of innerClassInfo array
* in correct order. The other InnerClassInfos are in reverse
* order in the rest of the innerClassInfo array.
*/
/* We now count the outerClasses. The reverse order is the
* right thing for us.
*/
{
String lastOuterName = getName();
for (int i = count - extraCount;
i < count && lastOuterName != null; i++) {
InnerClassInfo ici = innerClassInfo[i];
if (ici.inner.equals(lastOuterName)) {
outerCount++;
extraCount--;
lastOuterName = ici.outer;
}
}
}
if (innerCount > 0) {
innerClasses = new InnerClassInfo[innerCount];
System.arraycopy(innerClassInfo, 0,
innerClasses, 0, innerCount);
} else
innerClasses = null;
if (outerCount > 0) {
outerClasses = new InnerClassInfo[outerCount];
} else
outerClasses = null;
if (extraCount > 0) {
extraClasses = new InnerClassInfo[extraCount];
} else
extraClasses = null;
/* The last part: We split between outer and extra classes.
* In this step we will also revert the order of the extra
* classes.
*/
{
int outerPtr = 0;
String lastOuterName = getName();
for (int i = count - extraCount - outerCount;
i < count; i++) {
InnerClassInfo ici = innerClassInfo[i];
/* If we counted correctly there is no NullPointer
* or ArrayIndexOutOfBoundsException here
*/
if (ici.inner.equals(lastOuterName)) {
outerClasses[outerPtr++] = ici;
lastOuterName = ici.outer;
} else
extraClasses[--extraCount] = ici;
}
}
} else
super.readAttribute(name, length, cp, input, howMuch);
}
public void read(DataInputStream input, int howMuch) throws IOException {
/* Since we have to read the whole class anyway, we load all
* info, that we may need later and that does not take much memory.
*/
howMuch |= HIERARCHY | INNERCLASSES | OUTERCLASSES;
howMuch &= ~status;
/* header */
if (input.readInt() != 0xcafebabe)
throw new ClassFormatException("Wrong magic");
if (input.readUnsignedShort() > 3)
throw new ClassFormatException("Wrong minor");
if (input.readUnsignedShort() != 45)
throw new ClassFormatException("Wrong major");
/* constant pool */
ConstantPool cpool = new ConstantPool();
cpool.read(input);
/* always read modifiers, name, super, ifaces */
{
modifiers = input.readUnsignedShort();
String className = cpool.getClassName(input.readUnsignedShort());
if (!name.equals(className))
throw new ClassFormatException("wrong name " + className);
String superName = cpool.getClassName(input.readUnsignedShort());
superclass = superName != null ? ClassInfo.forName(superName) : null;
int count = input.readUnsignedShort();
interfaces = new ClassInfo[count];
for (int i=0; i< count; i++) {
interfaces[i] = ClassInfo.forName
(cpool.getClassName(input.readUnsignedShort()));
}
status |= HIERARCHY;
}
/* fields */
if ((howMuch & (FIELDS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
if ((status & FIELDS) == 0)
fields = new FieldInfo[count];
for (int i=0; i< count; i++) {
if ((status & FIELDS) == 0)
fields[i] = new FieldInfo(this);
fields[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
/* methods */
if ((howMuch & (METHODS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
int count = input.readUnsignedShort();
if ((status & METHODS) == 0)
methods = new MethodInfo[count];
for (int i=0; i< count; i++) {
if ((status & METHODS) == 0)
methods[i] = new MethodInfo(this);
methods[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
/* attributes */
readAttributes(cpool, input, howMuch);
status |= howMuch;
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i=0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp);
for (int i=0; i < methods.length; i++)
methods[i].reserveSmallConstants(gcp);
}
public void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name);
gcp.putClassName(superclass.getName());
for (int i=0; i < interfaces.length; i++)
gcp.putClassName(interfaces[i].getName());
for (int i=0; i < fields.length; i++)
fields[i].prepareWriting(gcp);
for (int i=0; i < methods.length; i++)
methods[i].prepareWriting(gcp);
if (sourceFile != null) {
gcp.putUTF8("SourceFile");
gcp.putUTF8(sourceFile);
}
if (outerClasses != null || innerClasses != null
|| extraClasses != null) {
gcp.putUTF8("InnerClasses");
int outerCount = outerClasses != null ? outerClasses.length : 0;
for (int i=outerCount; i-- > 0;) {
gcp.putClassName(outerClasses[i].inner);
if (outerClasses[i].outer != null)
gcp.putClassName(outerClasses[i].outer);
if (outerClasses[i].name != null)
gcp.putUTF8(outerClasses[i].name);
}
int innerCount = innerClasses != null ? innerClasses.length : 0;
for (int i=0; i< innerCount; i++) {
gcp.putClassName(innerClasses[i].inner);
if (innerClasses[i].outer != null)
gcp.putClassName(innerClasses[i].outer);
if (innerClasses[i].name != null)
gcp.putUTF8(innerClasses[i].name);
}
int extraCount = extraClasses != null ? extraClasses.length : 0;
for (int i=0; i< extraCount; i++) {
gcp.putClassName(extraClasses[i].inner);
if (extraClasses[i].outer != null)
gcp.putClassName(extraClasses[i].outer);
if (extraClasses[i].name != null)
gcp.putUTF8(extraClasses[i].name);
}
}
prepareAttributes(gcp);
}
protected int getKnownAttributeCount() {
int count = 0;
if (sourceFile != null)
count++;
if (innerClasses != null || outerClasses != null
|| extraClasses != null)
count++;
return count;
}
public void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (sourceFile != null) {
output.writeShort(gcp.putUTF8("SourceFile"));
output.writeInt(2);
output.writeShort(gcp.putUTF8(sourceFile));
}
if (outerClasses != null || innerClasses != null
|| extraClasses != null) {
output.writeShort(gcp.putUTF8("InnerClasses"));
int outerCount = (outerClasses != null) ? outerClasses.length : 0;
int innerCount = (innerClasses != null) ? innerClasses.length : 0;
int extraCount = (extraClasses != null) ? extraClasses.length : 0;
int count = outerCount + innerCount + extraCount;
output.writeInt(2 + count * 8);
output.writeShort(count);
for (int i=outerCount; i-- > 0; ) {
output.writeShort(gcp.putClassName(outerClasses[i].inner));
output.writeShort(outerClasses[i].outer != null ?
gcp.putClassName(outerClasses[i].outer) : 0);
output.writeShort(outerClasses[i].name != null ?
gcp.putUTF8(outerClasses[i].name) : 0);
output.writeShort(outerClasses[i].modifiers);
}
for (int i=0; i< innerCount; i++) {
output.writeShort(gcp.putClassName(innerClasses[i].inner));
output.writeShort(innerClasses[i].outer != null ?
gcp.putClassName(innerClasses[i].outer) : 0);
output.writeShort(innerClasses[i].name != null ?
gcp.putUTF8(innerClasses[i].name) : 0);
output.writeShort(innerClasses[i].modifiers);
}
for (int i=0; i< extraCount; i++) {
output.writeShort(gcp.putClassName(extraClasses[i].inner));
output.writeShort(extraClasses[i].outer != null ?
gcp.putClassName(extraClasses[i].outer) : 0);
output.writeShort(extraClasses[i].name != null ?
gcp.putUTF8(extraClasses[i].name) : 0);
output.writeShort(extraClasses[i].modifiers);
}
}
}
public void write(DataOutputStream out) throws IOException {
GrowableConstantPool gcp = new GrowableConstantPool();
reserveSmallConstants(gcp);
prepareWriting(gcp);
out.writeInt(0xcafebabe);
out.writeShort(3);
out.writeShort(45);
gcp.write(out);
out.writeShort(modifiers);
out.writeShort(gcp.putClassName(name));
out.writeShort(gcp.putClassName(superclass.getName()));
out.writeShort(interfaces.length);
for (int i=0; i < interfaces.length; i++)
out.writeShort(gcp.putClassName(interfaces[i].getName()));
out.writeShort(fields.length);
for (int i=0; i < fields.length; i++)
fields[i].write(gcp, out);
out.writeShort(methods.length);
for (int i=0; i < methods.length; i++)
methods[i].write(gcp, out);
writeAttributes(gcp, out);
}
public void loadInfoReflection(Class clazz, int howMuch)
throws SecurityException {
if ((howMuch & HIERARCHY) != 0) {
modifiers = clazz.getModifiers();
if (clazz.getSuperclass() == null)
superclass = clazz == Object.class ? null : javaLangObject;
else
superclass = ClassInfo.forName
(clazz.getSuperclass().getName());
Class[] ifaces = clazz.getInterfaces();
interfaces = new ClassInfo[ifaces.length];
for (int i=0; i<ifaces.length; i++)
interfaces[i] = ClassInfo.forName(ifaces[i].getName());
status |= HIERARCHY;
}
if ((howMuch & FIELDS) != 0 && fields == null) {
Field[] fs;
try {
fs = clazz.getDeclaredFields();
} catch (SecurityException ex) {
fs = clazz.getFields();
GlobalOptions.err.println
("Could only get public fields of class "
+ name + ".");
}
fields = new FieldInfo[fs.length];
for (int i = fs.length; --i >= 0; ) {
String type = TypeSignature.getSignature(fs[i].getType());
fields[i] = new FieldInfo
(this, fs[i].getName(), type, fs[i].getModifiers());
}
}
if ((howMuch & METHODS) != 0 && methods == null) {
Constructor[] cs;
Method[] ms;
try {
cs = clazz.getDeclaredConstructors();
ms = clazz.getDeclaredMethods();
} catch (SecurityException ex) {
cs = clazz.getConstructors();
ms = clazz.getMethods();
GlobalOptions.err.println
("Could only get public methods of class "
+ name + ".");
}
methods = new MethodInfo[cs.length + ms.length];
for (int i = cs.length; --i >= 0; ) {
String type = TypeSignature.getSignature
(cs[i].getParameterTypes(), void.class);
methods[i] = new MethodInfo
(this, "<init>", type, cs[i].getModifiers());
}
for (int i = ms.length; --i >= 0; ) {
String type = TypeSignature.getSignature
(ms[i].getParameterTypes(), ms[i].getReturnType());
methods[cs.length+i] = new MethodInfo
(this, ms[i].getName(), type, ms[i].getModifiers());
}
}
if ((howMuch & INNERCLASSES) != 0 && innerClasses == null) {
Class[] is;
try {
is = clazz.getDeclaredClasses();
} catch (SecurityException ex) {
is = clazz.getClasses();
GlobalOptions.err.println
("Could only get public inner classes of class "
+ name + ".");
}
if (is.length > 0) {
innerClasses = new InnerClassInfo[is.length];
for (int i = is.length; --i >= 0; ) {
String inner = is[i].getName();
int dollar = inner.lastIndexOf('$');
String name = inner.substring(dollar+1);
innerClasses[i] = new InnerClassInfo
(inner, getName(), name, is[i].getModifiers());
}
}
}
if ((howMuch & OUTERCLASSES) != 0 && outerClasses == null) {
int count = 0;
Class declarer = clazz.getDeclaringClass();
while (declarer != null) {
count++;
declarer = declarer.getDeclaringClass();
}
if (count > 0) {
outerClasses = new InnerClassInfo[count];
Class current = clazz;
for (int i = 0; i < count; i++) {
declarer = current.getDeclaringClass();
String name = current.getName();
int dollar = name.lastIndexOf('$');
outerClasses[i] = new InnerClassInfo
(name, declarer.getName(),
name.substring(dollar+1), current.getModifiers());
current = declarer;
}
}
}
status |= howMuch;
}
public void loadInfo(int howMuch) {
if ((status & howMuch) == howMuch)
return;
if (modified) {
System.err.println("Allocating info 0x"
+ Integer.toHexString(howMuch)
+ " (status 0x" + Integer.toHexString(status)
+ ") in class " + this);
Thread.dumpStack();
return;
}
try {
DataInputStream input = new DataInputStream
(new BufferedInputStream
(classpath.getFile(name.replace('.', '/') + ".class")));
read(input, howMuch);
} catch (IOException ex) {
String message = ex.getMessage();
if ((howMuch & ~(FIELDS|METHODS|HIERARCHY
|INNERCLASSES|OUTERCLASSES)) != 0) {
GlobalOptions.err.println
("Can't read class " + name + ".");
ex.printStackTrace(GlobalOptions.err);
throw new NoClassDefFoundError(name);
}
// Try getting the info through the reflection interface
// instead.
Class clazz = null;
try {
clazz = Class.forName(name);
} catch (ClassNotFoundException ex2) {
} catch (NoClassDefFoundError ex2) {
}
try {
if (clazz != null) {
loadInfoReflection(clazz, howMuch);
return;
}
} catch (SecurityException ex2) {
GlobalOptions.err.println
(ex2+" while collecting info about class " + name + ".");
}
// Give a warning and ``guess'' the hierarchie, methods etc.
GlobalOptions.err.println
("Can't read class " + name + ", types may be incorrect. ("
+ ex.getClass().getName()
+ (message != null ? ": " + message : "") + ")");
ex.printStackTrace(GlobalOptions.err);
if ((howMuch & HIERARCHY) != 0) {
modifiers = Modifier.PUBLIC;
if (name.equals("java.lang.Object"))
superclass = null;
else
superclass = javaLangObject;
interfaces = new ClassInfo[0];
}
if ((howMuch & METHODS) != 0)
methods = new MethodInfo[0];
if ((howMuch & FIELDS) != 0)
fields = new FieldInfo[0];
status |= howMuch;
}
}
/**
* This is the counter part to loadInfo. It will drop all info specified
* in howMuch and clean up the memory.
* @param howMuch tells how much info we should drop
*/
public void dropInfo(int howMuch) {
if ((status & howMuch) == 0)
return;
if (modified) {
System.err.println("Dropping info 0x"
+ Integer.toHexString(howMuch)
+ " (status 0x" + Integer.toHexString(status)
+ ") in class " + this);
Thread.dumpStack();
return;
}
howMuch &= status;
if ((howMuch & FIELDS) != 0) {
fields = null;
} else if ((status & FIELDS) != 0
&& (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
for (int i=0; i < fields.length; i++)
fields[i].dropInfo(howMuch);
}
if ((howMuch & METHODS) != 0) {
methods = null;
} else if ((status & METHODS) != 0
&& (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) {
for (int i=0; i < methods.length; i++)
methods[i].dropInfo(howMuch);
}
if ((howMuch & KNOWNATTRIBS) != 0)
sourceFile = null;
if ((howMuch & OUTERCLASSES) != 0)
outerClasses = null;
if ((howMuch & INNERCLASSES) != 0) {
innerClasses = null;
extraClasses = null;
}
super.dropInfo(howMuch);
status &= ~howMuch;
}
public String getName() {
return name;
}
public String getJavaName() {
/* Don't load attributes for class names not containing a
* dollar sign.
*/
if (name.indexOf('$') == -1)
return getName();
if (getOuterClasses() != null) {
int last = outerClasses.length-1;
StringBuffer sb =
new StringBuffer(outerClasses[last].outer != null
? outerClasses[last].outer : "METHOD");
for (int i=last; i >= 0; i--)
sb.append(".").append(outerClasses[i].name != null
? outerClasses[i].name : "ANONYMOUS");
return sb.toString();
}
return getName();
}
public ClassInfo getSuperclass() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return superclass;
}
public ClassInfo[] getInterfaces() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return interfaces;
}
public int getModifiers() {
if ((status & HIERARCHY) == 0)
loadInfo(HIERARCHY);
return modifiers;
}
public boolean isInterface() {
return Modifier.isInterface(getModifiers());
}
public FieldInfo findField(String name, String typeSig) {
if ((status & FIELDS) == 0)
loadInfo(FIELDS);
for (int i=0; i< fields.length; i++)
if (fields[i].getName().equals(name)
&& fields[i].getType().equals(typeSig))
return fields[i];
return null;
}
public MethodInfo findMethod(String name, String typeSig) {
if ((status & METHODS) == 0)
loadInfo(METHODS);
for (int i=0; i< methods.length; i++)
if (methods[i].getName().equals(name)
&& methods[i].getType().equals(typeSig))
return methods[i];
return null;
}
public MethodInfo[] getMethods() {
if ((status & METHODS) == 0)
loadInfo(METHODS);
return methods;
}
public FieldInfo[] getFields() {
if ((status & FIELDS) == 0)
loadInfo(FIELDS);
return fields;
}
public InnerClassInfo[] getOuterClasses() {
if ((status & OUTERCLASSES) == 0)
loadInfo(OUTERCLASSES);
return outerClasses;
}
public InnerClassInfo[] getInnerClasses() {
if ((status & INNERCLASSES) == 0)
loadInfo(INNERCLASSES);
return innerClasses;
}
public InnerClassInfo[] getExtraClasses() {
if ((status & INNERCLASSES) == 0)
loadInfo(INNERCLASSES);
return extraClasses;
}
public String getSourceFile() {
return sourceFile;
}
public void setName(String newName) {
name = newName;
modified = true;
}
public void setSuperclass(ClassInfo newSuper) {
superclass = newSuper;
modified = true;
}
public void setInterfaces(ClassInfo[] newIfaces) {
interfaces = newIfaces;
modified = true;
}
public void setModifiers(int newModifiers) {
modifiers = newModifiers;
modified = true;
}
public void setMethods(MethodInfo[] mi) {
methods = mi;
modified = true;
}
public void setFields(FieldInfo[] fi) {
fields = fi;
modified = true;
}
public void setOuterClasses(InnerClassInfo[] oc) {
outerClasses = oc;
modified = true;
}
public void setInnerClasses(InnerClassInfo[] ic) {
innerClasses = ic;
modified = true;
}
public void setExtraClasses(InnerClassInfo[] ec) {
extraClasses = ec;
modified = true;
}
public void setSourceFile(String newSource) {
sourceFile = newSource;
modified = true;
}
public boolean superClassOf(ClassInfo son) {
while (son != this && son != null) {
son = son.getSuperclass();
}
return son == this;
}
public boolean implementedBy(ClassInfo clazz) {
while (clazz != this && clazz != null) {
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i< ifaces.length; i++) {
if (implementedBy(ifaces[i]))
return true;
}
clazz = clazz.getSuperclass();
}
return clazz == this;
}
public String toString() {
return name;
}
}

@ -0,0 +1,829 @@
/* ClassPath Copyright (C) 1998-1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.ByteArrayInputStream;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
import jode.GlobalOptions;
import jode.util.UnifyHash;
/**
* A path in which class files are searched for.
*
* Class files can be loaded from several different locations, these
* locations can be:
* <ul>
* <li> A local directory.</li>
* <li> A local jar or zip file </li>
* <li> A URL (unified resource location), pointing to a directory </li>
* <li> A URL pointing to a jar or zip file. </li>
* <li> A Jar URL (see {@link java.net.JarURLConnection}), useful if
* the jar file is not packed correctly</li>
* <li> The reflection URL <code>reflection:/</code> This location can
* only load declarations of classes. If a security manager is
* present, it can only load public declarations.</li>
* </ul>
*
* We use standard java means to find a class file: package correspong
* to directories and the class file must have the <code>.class</code>
* extension. For example if the class path points to
* <code>/home/java</code>, the class <code>java.lang.Object</code> is
* loaded from <code>/home/java/java/lang/Object.class</code>.
*
* A class path can have another ClassPath as fallback. If
* ClassInfo.loadInfo is called and the class isn't found the fallback
* ClassPath is searched instead. This repeats until there is no
* further fallback.
*
* The main method for creating classes is {@link #getClassInfo}. The
* other available methods are useful to find other files in the
* ClassPath and to get a listing of all available files and classes.
*
* A ClassPath handles some <code>IOException</code>s and
* <code>SecurityException</code>s skipping the path that produced
* them.
*
* @author Jochen Hoenicke
* @version 1.1 */
public class ClassPath {
/**
* We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs.
*
* We currently allow both pathSeparatorChar and
* altPathSeparatorChar and decide if it is a protocol separator
* by context. This doesn't always work, so use
* <code>altPathSeparator</code>, or the ClassPath(String[])
* constructor.
*/
public static final char altPathSeparatorChar = ',';
private class Path {
public boolean exists(String file) {
return false;
}
public boolean isDirectory(String file) {
return false;
}
public InputStream getFile(String file) throws IOException {
return null;
}
public Enumeration listFiles(String directory) {
return null;
}
public boolean loadClass(ClassInfo clazz, int howMuch)
throws IOException, ClassFormatException
{
String file = clazz.getName().replace('.', '/') + ".class";
if (!exists(file))
return false;
DataInputStream input = new DataInputStream
(new BufferedInputStream
(getFile(file)));
clazz.read(input, howMuch);
return true;
}
}
private class ReflectionPath extends Path {
public boolean loadClass(ClassInfo classinfo, int howMuch)
throws IOException, ClassFormatException
{
if (howMuch > ClassInfo.DECLARATIONS)
return false;
Class clazz = null;
try {
clazz = Class.forName(classinfo.getName());
} catch (ClassNotFoundException ex) {
return false;
} catch (NoClassDefFoundError ex) {
return false;
}
try {
classinfo.loadFromReflection(clazz, howMuch);
return true;
} catch (SecurityException ex) {
return false;
}
}
}
private class LocalPath extends Path {
private File dir;
private LocalPath(File path) {
dir = path;
}
public boolean exists(String filename) {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
try {
return new File(dir, filename).exists();
} catch (SecurityException ex) {
return false;
}
}
public boolean isDirectory(String filename) {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
return new File(dir, filename).isDirectory();
}
public InputStream getFile(String filename) throws IOException {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
File f = new File(dir, filename);
return new FileInputStream(f);
}
public Enumeration listFiles(String directory) {
if (File.separatorChar != '/')
directory = directory
.replace('/', File.separatorChar);
File f = new File(dir, directory);
final String[] files = f.list();
if (files == null)
return null;
if (!directory.endsWith(File.separator))
directory += File.separator;
final String prefix = directory;
return new Enumeration() {
int i = 0;
public boolean hasMoreElements() {
return i < files.length;
}
public Object nextElement() {
try {
return files[i++];
} catch (ArrayIndexOutOfBoundsException ex) {
return new NoSuchElementException();
}
}
};
}
}
private class ZipPath extends Path {
private Hashtable entries = new Hashtable();
private ZipFile file;
private byte[] contents;
private String prefix;
private void addEntry(ZipEntry ze) {
String name = ze.getName();
if (prefix != null) {
if (!name.startsWith(prefix))
return;
name = name.substring(prefix.length());
}
if (ze.isDirectory()
/* || !name.endsWith(".class")*/)
return;
do {
String dir = "";
int pathsep = name.lastIndexOf("/");
if (pathsep != -1) {
dir = name.substring(0, pathsep);
name = name.substring(pathsep+1);
}
Vector dirContent = (Vector) entries.get(dir);
if (dirContent != null) {
dirContent.addElement(name);
return;
}
dirContent = new Vector();
dirContent.addElement(name);
entries.put(dir, dirContent);
name = dir;
} while (name.length() > 0);
}
private ZipPath(ZipFile zipfile, String prefix) {
this.file = zipfile;
this.prefix = prefix;
Enumeration zipEnum = file.entries();
entries = new Hashtable();
while (zipEnum.hasMoreElements()) {
addEntry((ZipEntry) zipEnum.nextElement());
}
}
private ZipPath(byte[] zipcontents, String prefix)
throws IOException
{
this.contents = zipcontents;
this.prefix = prefix;
// fill entries into hash table
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(zipcontents));
entries = new Hashtable();
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
addEntry(ze);
zis.closeEntry();
}
zis.close();
}
public boolean exists(String filename) {
if (entries.containsKey(filename))
return true;
String dir = "";
String name = filename;
int index = filename.lastIndexOf('/');
if (index >= 0) {
dir = filename.substring(0, index);
name = filename.substring(index+1);
}
Vector directory = (Vector)entries.get(dir);
if (directory != null && directory.contains(name))
return true;
return false;
}
public boolean isDirectory(String filename) {
return entries.containsKey(filename);
}
public InputStream getFile(String filename) throws IOException {
String fullname = prefix != null ? prefix + filename : filename;
if (contents != null) {
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(contents));
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
if (ze.getName().equals(fullname)) {
///#ifdef JDK11
/// // The skip method in jdk1.1.7 ZipInputStream
/// // is buggy. We return a wrapper that fixes
/// // this.
/// return new FilterInputStream(zis) {
/// private byte[] tmpbuf = new byte[512];
/// public long skip(long n) throws IOException {
/// long skipped = 0;
/// while (n > 0) {
/// int count = read(tmpbuf, 0,
/// (int)Math.min(n, 512L));
/// if (count == -1)
/// return skipped;
/// skipped += count;
/// n -= count;
/// }
/// return skipped;
/// }
/// };
///#else
return zis;
///#endif
}
zis.closeEntry();
}
zis.close();
} else {
ZipEntry ze = file.getEntry(fullname);
if (ze != null)
return file.getInputStream(ze);
}
return null;
}
public Enumeration listFiles(String directory) {
Vector direntries = (Vector) entries.get(directory);
if (direntries != null)
return direntries.elements();
return null;
}
}
private class URLPath extends Path {
URL base;
private URLPath(URL base) {
this.base = base;
}
public boolean exists(String filename) {
try {
URL url = new URL(base, filename);
URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
return true;
} catch (IOException ex) {
return false;
}
}
public InputStream getFile(String filename) throws IOException {
try {
URL url = new URL(base, filename);
URLConnection conn = url.openConnection();
conn.setAllowUserInteraction(true);
return conn.getInputStream();
} catch (IOException ex) {
return null;
}
}
public boolean loadClass(ClassInfo clazz, int howMuch)
throws IOException, ClassFormatException
{
String file = clazz.getName().replace('.', '/') + ".class";
InputStream is = getFile(file);
if (is == null)
return false;
DataInputStream input = new DataInputStream
(new BufferedInputStream(is));
clazz.read(input, howMuch);
return true;
}
}
private Path[] paths;
private UnifyHash classes = new UnifyHash();
ClassPath fallback = null;
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
* supported.
* @param path An array of paths.
* @param fallback The fallback classpath.
*/
public ClassPath(String[] paths, ClassPath fallback) {
this.fallback = fallback;
initPath(paths);
}
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
* supported.
* @param path An array of paths.
*/
public ClassPath(String[] paths) {
initPath(paths);
}
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
* supported.
* @param path One or more paths. They should be separated by the
* altPathSeparatorChar or pathSeparatorChar, but the latter is
* deprecated since it may give problems for UNIX machines.
* @see #ClassPath(String[] paths)
*/
public ClassPath(String path, ClassPath fallback) {
this(path);
this.fallback = fallback;
}
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
* supported.
* @param path One or more paths. They should be separated by the
* altPathSeparatorChar or pathSeparatorChar, but the latter is
* deprecated since it may give problems for UNIX machines.
* @see #ClassPath(String[] paths)
*/
public ClassPath(String path) {
// Calculate a good approximation (rounded upwards) of the tokens
// in this path.
int length = 1;
for (int index=path.indexOf(File.pathSeparatorChar);
index != -1; length++)
index = path.indexOf(File.pathSeparatorChar, index+1);
if (File.pathSeparatorChar != altPathSeparatorChar) {
for (int index=path.indexOf(altPathSeparatorChar);
index != -1; length++)
index = path.indexOf(altPathSeparatorChar, index+1);
}
String[] tokens = new String[length];
int i = 0;
for (int ptr=0; ptr < path.length(); ptr++, i++) {
int next = ptr;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
int index = ptr;
colon_separator:
while (next > ptr
&& next < path.length()
&& path.charAt(next) == ':') {
// Check if this is a URL instead of a pathSeparator
// Since this is a while loop it allows nested urls like
// jar:ftp://ftp.foo.org/pub/foo.jar!/
while (index < next) {
char c = path.charAt(index);
// According to RFC 1738 letters, digits, '+', '-'
// and '.' are allowed SCHEMA characters. We
// disallow '.' because it is a good marker that
// the user has specified a filename instead of a
// URL.
if ((c < 'A' || c > 'Z')
&& (c < 'a' || c > 'z')
&& (c < '0' || c > '9')
&& "+-".indexOf(c) == -1) {
break colon_separator;
}
index++;
}
next++;
index++;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
}
tokens[i] = path.substring(ptr, next);
ptr = next;
}
initPath(tokens);
}
private byte[] readURLZip(URLConnection conn) {
int length = conn.getContentLength();
if (length <= 0)
// Give a approximation if length is unknown
length = 10240;
else
// Increase the length by one, so we hopefully don't need
// to grow the array later (we need a little overshot to
// know when the end is reached).
length++;
byte[] contents = new byte[length];
try {
InputStream is = conn.getInputStream();
int pos = 0;
for (;;) {
// This is ugly, is.available() may return zero even
// if there are more bytes.
int avail = Math.max(is.available(), 1);
if (pos + is.available() > contents.length) {
// grow the byte array.
byte[] newarr = new byte
[Math.max(2*contents.length, pos + is.available())];
System.arraycopy(contents, 0, newarr, 0, pos);
contents = newarr;
}
int count = is.read(contents, pos, contents.length-pos);
if (count == -1)
break;
pos += count;
}
if (pos < contents.length) {
// shrink the byte array again.
byte[] newarr = new byte[pos];
System.arraycopy(contents, 0, newarr, 0, pos);
contents = newarr;
}
return contents;
} catch (IOException ex) {
return null;
}
}
private void initPath(String[] tokens) {
int length = tokens.length;
paths = new Path[length];
for (int i = 0; i < length; i++) {
String path = tokens[i];
if (path == null)
continue;
String zipPrefix = null;
// The special reflection URL
if (path.startsWith("reflection:")) {
paths[i] = new ReflectionPath();
continue;
}
// We handle jar URL's ourself.
if (path.startsWith("jar:")) {
int index = 0;
do {
index = path.indexOf('!', index);
} while (index != -1 && index != path.length()-1
&& path.charAt(index+1) != '/');
if (index == -1 || index == path.length() - 1) {
GlobalOptions.err.println("Warning: Illegal jar url "
+ path + ".");
continue;
}
zipPrefix = path.substring(index+2);
if (!zipPrefix.endsWith("/"))
zipPrefix += "/";
path = path.substring(4, index);
}
int index = path.indexOf(':');
if (index != -1 && index < path.length()-2
&& path.charAt(index+1) == '/'
&& path.charAt(index+2) == '/') {
// This looks like an URL.
try {
URL base = new URL(path);
try {
URLConnection connection = base.openConnection();
if (zipPrefix != null
|| path.endsWith(".zip") || path.endsWith(".jar")
|| connection.getContentType().endsWith("/zip")) {
// This is a zip file. Read it into memory.
byte[] contents = readURLZip(connection);
if (contents != null)
paths[i] = new ZipPath(contents, zipPrefix);
} else
paths[i] = new URLPath(base);
} catch (IOException ex) {
GlobalOptions.err.println
("Warning: IO exception while accessing "
+path+".");
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: Security exception while accessing "
+path+".");
}
} catch (MalformedURLException ex) {
GlobalOptions.err.println
("Warning: Malformed URL "+ path + ".");
}
} else {
try {
File dir = new File(path);
if (zipPrefix != null || !dir.isDirectory()) {
try {
paths[i] = new ZipPath(new ZipFile(dir),
zipPrefix);
} catch (java.io.IOException ex) {
GlobalOptions.err.println
("Warning: Can't read "+ path + ".");
}
} else
paths[i] = new LocalPath(dir);
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: SecurityException while accessing "
+ path + ".");
}
}
}
}
/**
* Creates a new class info for a class residing in this search
* path. This doesn't load the class immediately, this is done by
* ClassInfo.loadInfo. It is no error if class doesn't exists.
* @param classname the dot-separated full qualified name of the class.
* For inner classes you must use the bytecode name with $,
* e.g. <code>java.util.Map$Entry.</code>
* @exception IllegalArgumentException if class name isn't valid.
*/
public ClassInfo getClassInfo(String classname)
{
checkClassName(classname);
int hash = classname.hashCode();
Iterator iter = classes.iterateHashCode(hash);
while (iter.hasNext()) {
ClassInfo clazz = (ClassInfo) iter.next();
if (clazz.getName().equals(classname))
return clazz;
}
ClassInfo clazz = new ClassInfo(classname, this);
classes.put(hash, clazz);
return clazz;
}
/**
* Checks, if a class with the given name exists somewhere in this
* path.
* @param classname the class name.
* @exception IllegalArgumentException if class name isn't valid.
*/
public boolean existsClass(String classname) {
checkClassName(classname);
return existsFile(classname.replace('.', '/') + ".class");
}
/**
* Checks, if a file with the given name exists somewhere in this
* path.
* @param filename the file name.
* @see #existsClass
*/
public boolean existsFile(String filename) {
for (int i=0; i<paths.length; i++) {
if (paths[i] != null && paths[i].exists(filename))
return true;
}
return false;
}
private void checkClassName(String name) {
if (name == null
|| name.indexOf(';') != -1
|| name.indexOf('[') != -1
|| name.indexOf('/') != -1)
throw new IllegalArgumentException("Illegal class name: "+name);
}
/**
* Searches for a file in the class path.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return An InputStream for the file.
*/
public InputStream getFile(String filename) throws IOException {
for (int i=0; i < paths.length; i++) {
if (paths[i] != null && paths[i].exists(filename))
return paths[i].getFile(filename);
}
throw new FileNotFoundException(filename);
}
/**
* Searches for a filename in the class path and tells if it is a
* directory.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return true, if filename exists and is a directory, false otherwise.
*/
public boolean isDirectory(String filename) {
for (int i=0; i < paths.length; i++) {
if (paths[i] != null && paths[i].exists(filename))
return paths[i].isDirectory(filename);
}
return false;
}
/**
* Searches for a filename in the class path and tells if it is a
* package. This is the same as isDirectory.
* @param fqn the full qualified name. The components should be dot
* separated.
* @return true, if filename exists and is a package, false otherwise.
* @see isDirectory
*/
public boolean isPackage(String fqn) {
return isDirectory(fqn.replace('.', '/'));
}
/**
* Get a list of all files in a given directory.
* @param dirName the directory name. The path components must
* be separated by <code>/</code>.
* @return An enumeration with all files/directories in the given
* directory. If dirName doesn't denote a directory it returns null.
*/
public Enumeration listFiles(final String dirName) {
return new Enumeration() {
int i = 0;
Enumeration enum;
public boolean hasMoreElements() {
while (true) {
while (enum == null && i < paths.length) {
if (paths[i] != null && paths[i].exists(dirName)
&& paths[i].isDirectory(dirName))
enum = paths[i].listFiles(dirName);
i++;
}
if (enum == null)
return false;
if (enum.hasMoreElements())
return true;
enum = null;
}
}
public Object nextElement() {
if (!hasMoreElements())
return new NoSuchElementException();
return enum.nextElement();
}
};
}
/**
* Get a list of all classes and packages in the given package.
* @param package a dot-separated package name.
* @return An enumeration with all class/subpackages in the given
* package. If package doesn't denote a package it returns null.
*/
public Enumeration listClassesAndPackages(String packageName) {
String dir = packageName.replace('.','/');
final Enumeration enum = listFiles(dir);
final String prefix = dir.length() > 0 ? dir + "/" : dir;
return new Enumeration() {
String next = getNext();
private String getNext() {
while (enum.hasMoreElements()) {
String name = (String) enum.nextElement();
if (name.indexOf('.') == -1
&& isDirectory(prefix + name))
// This is a package
return name;
if (name.endsWith(".class"))
// This is a class
return name.substring(0, name.length()-6);
}
return null;
}
public boolean hasMoreElements() {
return next != null;
}
public Object nextElement() {
if (next == null)
throw new NoSuchElementException();
String result = next;
next = getNext();
return result;
}
};
}
boolean loadClass(ClassInfo clazz, int howMuch)
throws IOException, ClassFormatException
{
for (int i = 0; i < paths.length; i++) {
if (paths[i] != null && paths[i].loadClass(clazz, howMuch))
return true;
}
if (fallback != null)
return fallback.loadClass(clazz, howMuch);
return false;
}
}

@ -0,0 +1,64 @@
/* ConstantInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.util.StringQuoter;
/**
* This class represents an instruction in the byte code.
*
*/
public class ConstantInstruction extends Instruction {
/**
* The typesignature of the class/array.
*/
private Object constant;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public ConstantInstruction(int opcode, Object constant, int lineNr) {
super(opcode, lineNr);
if (opcode != opc_ldc && opcode != opc_ldc2_w)
throw new IllegalArgumentException("Instruction has no typesig");
this.constant = constant;
}
public ConstantInstruction(int opcode, Object constant) {
this(opcode, constant, -1);
}
public final Object getConstant()
{
return constant;
}
public final void setConstant(Object constant)
{
this.constant = constant;
}
public String toString() {
return super.toString() + ' ' +
(constant instanceof String
? StringQuoter.quote((String) constant) : constant);
}
}

@ -26,7 +26,7 @@ import java.io.IOException;
*
* @author Jochen Hoenicke
*/
public class ConstantPool {
class ConstantPool {
public final static int CLASS = 7;
public final static int FIELDREF = 9;
public final static int METHODREF = 10;

@ -22,10 +22,11 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
///#def COLLECTIONEXTRA java.lang
import java.lang.Comparable;
///#enddef
public class FieldInfo extends BinaryInfo {
ClassInfo clazzInfo;
public final class FieldInfo extends BinaryInfo implements Comparable {
int modifier;
String name;
String typeSig;
@ -34,12 +35,10 @@ public class FieldInfo extends BinaryInfo {
boolean syntheticFlag;
boolean deprecatedFlag;
public FieldInfo(ClassInfo ci) {
this.clazzInfo = ci;
public FieldInfo() {
}
public FieldInfo(ClassInfo ci, String name, String typeSig, int modifier) {
this.clazzInfo = ci;
public FieldInfo(String name, String typeSig, int modifier) {
this.name = name;
this.typeSig = typeSig;
this.modifier = modifier;
@ -49,10 +48,11 @@ public class FieldInfo extends BinaryInfo {
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("ConstantValue")) {
if (howMuch >= ClassInfo.DECLARATIONS
&& name.equals("ConstantValue")) {
if (length != 2)
throw new ClassFormatException("ConstantValue attribute"
+ " has wrong length");
throw new ClassFormatException
("ConstantValue attribute has wrong length");
int index = input.readUnsignedShort();
constant = cp.getConstant(index);
} else if (name.equals("Synthetic")) {
@ -140,10 +140,8 @@ public class FieldInfo extends BinaryInfo {
writeAttributes(constantPool, output);
}
public void dropInfo(int howMuch) {
if ((howMuch & KNOWNATTRIBS) != 0)
constant = null;
super.dropInfo(howMuch);
public void dropBody() {
super.dropAttributes();
}
public String getName() {
@ -167,7 +165,6 @@ public class FieldInfo extends BinaryInfo {
}
public Object getConstant() {
clazzInfo.loadInfo(KNOWNATTRIBS);
return constant;
}
@ -195,6 +192,26 @@ public class FieldInfo extends BinaryInfo {
constant = newConstant;
}
/**
* Compares two FieldInfo objects for field order. The field
* order is as follows: First the static class intializer followed
* by constructor with type signature sorted lexicographic. Then
* all other fields sorted lexicographically by name. If two
* fields have the same name, they are sorted by type signature,
* though that can only happen for obfuscated code.
*
* @return a positive number if this field follows the other in
* field order, a negative number if it preceeds the
* other, and 0 if they are equal.
* @exception ClassCastException if other is not a ClassInfo. */
public int compareTo(Object other) {
FieldInfo fi = (FieldInfo) other;
int result = name.compareTo(fi.name);
if (result == 0)
result = typeSig.compareTo(fi.typeSig);
return result;
}
public String toString() {
return "Field "+Modifier.toString(modifier)+" "+
typeSig+" "+name;

@ -27,7 +27,7 @@ import java.util.Hashtable;
*
* @author Jochen Hoenicke
*/
public class GrowableConstantPool extends ConstantPool {
class GrowableConstantPool extends ConstantPool {
Hashtable entryToIndex = new Hashtable();
boolean written;
@ -243,6 +243,8 @@ public class GrowableConstantPool extends ConstantPool {
public void write(DataOutputStream stream)
throws IOException {
written = true;
if (count > 65536)
throw new ClassFormatError("Too many constants");
stream.writeShort(count);
for (int i=1; i< count; i++) {
int tag = tags[i];

@ -20,10 +20,75 @@
package jode.bytecode;
/**
* A simple class containing the info about an exception handler
* A simple class containing the info about one try-catch block.
*
* @author Jochen Hoenicke
*/
public class Handler {
public Instruction start, end, catcher;
public String type;
Block start, end, catcher;
String type;
/**
* The empty handler array. Since handlers are often empty, we don't
* want to create a new object each time.
*/
final static Handler[] EMPTY = new Handler[0];
/**
* Creates a new handler.
*/
Handler(Block s, Block e, Block c, String t) {
start = s;
end = e;
catcher = c;
type = t;
}
/**
* Gets the first basic block of the try.
*/
public Block getStart() {
return start;
}
/**
* Gets the last basic block of the try.
*/
public Block getEnd() {
return end;
}
/**
* Gets the first basic block of the exception handler.
*/
public Block getCatcher() {
return catcher;
}
/**
* Gets the type signature of the exception.
*/
public String getType() {
return type;
}
public void setStart(Block start) {
this.start = start;
}
public void setEnd(Block end) {
this.end = end;
}
public void setCatcher(Block catcher) {
this.catcher = catcher;
}
/**
* Sets the type signature of the exception.
*/
public void setType(String type) {
this.type = type;
}
}

@ -0,0 +1,70 @@
/* IncInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. 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 class IncInstruction extends SlotInstruction {
/**
* The amount of increment.
*/
private int increment;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public IncInstruction(int opcode, int slot, int increment, int lineNr) {
super(opcode, slot, lineNr);
if (opcode != opc_iinc)
throw new IllegalArgumentException("Instruction has no increment");
this.increment = increment;
}
/**
* Creates a simple opcode, without any parameters.
*/
public IncInstruction(int opcode, int slot, int increment) {
this(opcode, slot, increment, -1);
}
/**
* Get the increment for an opc_iinc instruction.
*/
public final int getIncrement()
{
return increment;
}
/**
* Set the increment for an opc_iinc instruction.
*/
public final void setIncrement(int incr)
{
this.increment = incr;
}
public String toString() {
return super.toString()+' '+increment;
}
}

@ -1,42 +0,0 @@
/* InnerClassInfo Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* A simple class containing the info about an inner class.
*/
public class InnerClassInfo {
public String inner, outer;
public String name;
public int modifiers;
public InnerClassInfo(String inner, String outer, String name, int modif) {
this.inner = inner;
this.outer = outer;
this.name = name;
this.modifiers = modif;
}
public String toString() {
return "InnerClassInfo["+inner+","+outer+","+name+","
+java.lang.reflect.Modifier.toString(modifiers)+"]";
}
}

@ -23,268 +23,215 @@ package jode.bytecode;
* This class represents an instruction in the byte code.
*
*/
public final class Instruction implements Opcodes{
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>
* The opcode and lineNr of the instruction.
* opcode is <code>(lineAndOpcode &amp; 0xff)</code>, while
* lineNr is <code>(lineAndOpcode &gt;&gt; 8)</code>.
* If line number is not known or unset, it is -1.
*/
// 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;
private int lineAndOpcode;
// /**
// * Optional object data for this opcode. There are six 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</dt>
// * <dd>The typesignature of the class/array</dd>
// * <dt>opc_lookupswitch</dt>
// * <dd>The array of values of type int[]</dd>
// * <dt>opc_multianewarray</dt>
// * <dd>A DoubleParam: intValue contains dimension, objValue contains
// * reference </dd>
// * <dt>opc_[aildf]{load,store}</dt>
// * <dd>The LocalVariableInfo</dd>
// * <dt>opc_iinc</dt>
// * <dd>A DoubleParam: intValue contains count, objValue contains
// * local variable info.</dd>
// * </dl>
// */
// private Object param;
// /**
// * Create a new Instruction suitable for the given opcode. We map
// * some opcodes, so you must always use the mapped opcode.
// * <pre>
// * [iflda]load_x -&gt; [iflda]load
// * [iflda]store_x -&gt; [iflda]store
// * [ifa]const_xx, ldc_w -&gt; ldc
// * [dl]const_xx -&gt; ldc2_w
// * wide opcode -&gt; opcode
// * tableswitch -&gt; lookupswitch
// * [a]newarray -&gt; multianewarray
// * </pre>
// */
// public static Instruction forOpcode(int opcode) {
// if (opcode == opc_iinc)
// return new IncInstruction(opcode);
// else if (opcode == opc_ret
// || opcode >= opc_iload && opcode <= opc_aload
// || opcode >= opc_istore && opcode <= opc_astore)
// return new SlotInstruction(opcode);
// else if (opcode >= opc_getstatic && opcode <= opc_invokeinterface)
// return new ReferenceInstruction(opcode);
// else switch (opcode) {
// case opc_new:
// case opc_checkcast:
// case opc_instanceof:
// return new TypeInstruction(opcode);
// case opc_multianewarray:
// return new TypeDimensionInstruction(opcode);
// default:
// return new Instruction(opcode);
// }
// }
/**
* The previous instruction in code order, useful when changing
* the order.
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
Instruction prevByAddr;
public Instruction(int opcode, int lineNr) {
if (stackDelta.charAt(opcode) == '\177')
throw new IllegalArgumentException("Unknown opcode: "+opcode);
this.lineAndOpcode = (lineNr << 8) | opcode;
}
/**
* 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!
* Creates a simple opcode, without any parameters.
*/
private Object tmpInfo;
public Instruction(int opcode) {
this.opcode = opcode;
this(opcode, -1);
}
/**
* Returns the opcode of the instruction. We map some opcodes:
* <pre>
* [iflda]load_x -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; multianewarray
* </pre>
* Returns the opcode of the instruction.
*/
public final int getOpcode() {
return opcode;
return lineAndOpcode & 0xff;
}
/**
* 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 boolean hasLineNr() {
return lineAndOpcode >= 0;
}
public final int getNextAddr() {
return nextByAddr.addr;
public final int getLineNr() {
return lineAndOpcode >> 8;
}
/**
* 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;
public final void setLineNr(int nr) {
lineAndOpcode = (nr << 8) | (lineAndOpcode & 0xff);
}
final void setAddr(int addr) {
this.addr = addr;
public boolean isStore() {
return false;
}
public final boolean hasLocalSlot() {
return opcode == opc_iinc || opcode == opc_ret
|| opcode >= opc_iload && opcode <= opc_aload
|| opcode >= opc_istore && opcode <= opc_astore;
public boolean hasLocal() {
return false;
}
public final int getLocalSlot()
/*{ require { hasLocalSlot()
:: "Instruction has no slot" } }*/
public int getLocalSlot()
{
return shortData;
throw new IllegalArgumentException();
// UnsupportedOperationException would be more appropriate
}
public final void setLocalSlot(int slot)
/*{ require { hasLocalSlot()
:: "Instruction has no slot" } }*/
public LocalVariableInfo getLocalInfo()
{
shortData = slot;
throw new IllegalArgumentException();
}
public void setLocalInfo(LocalVariableInfo info)
{
throw new IllegalArgumentException();
}
public void setLocalSlot(int slot)
{
throw new IllegalArgumentException();
}
/**
* 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>
* Get the increment for an opc_iinc instruction.
*/
public final int getIncrement()
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
|| opcode == opc_tableswitch
:: "Instruction has no int data" } }*/
public int getIncrement()
{
/* shortData already used for local slot */
return ((Short) objData).shortValue();
throw new IllegalArgumentException();
}
/**
* 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>
* Set the increment for an opc_iinc instruction.
*/
public final void setIncrement(int incr)
/*{ require { opcode == opc_iinc || opcode == opc_multianewarray
:: "Instruction has no int data" } }*/
public void setIncrement(int incr)
{
/* shortData already used for local slot */
objData = new Short((short) incr);
throw new IllegalArgumentException();
}
/**
*
* Get the dimensions for an opc_anewarray opcode.
*/
public final int getDimensions()
/*{ require { opcode == opc_multianewarray
:: "Instruction has no dimensions" } }*/
public int getDimensions()
{
return shortData;
throw new IllegalArgumentException();
}
/**
*
* Set the dimensions for an opc_anewarray opcode.
*/
public final void setDimensions(int dims)
/*{ require { opcode == opc_multianewarray
:: "Instruction has no dimensions" } }*/
public void setDimensions(int dims)
{
shortData = dims;
throw new IllegalArgumentException();
}
public final Object getConstant()
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
:: "Instruction has no constant" } }*/
public Object getConstant()
{
return objData;
throw new IllegalArgumentException();
}
public final void setConstant(Object constant)
/*{ require { opcode == opc_ldc || opcode == opc_ldc2_w
:: "Instruction has no constant" } }*/
public void setConstant(Object constant)
{
objData = constant;
throw new IllegalArgumentException();
}
public final Reference getReference()
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
:: "Instruction has no reference" } }*/
public Reference getReference()
{
return (Reference) objData;
throw new IllegalArgumentException();
}
public final void setReference(Reference ref)
/*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface
:: "Instruction has no reference" } }*/
public void setReference(Reference ref)
{
objData = ref;
throw new IllegalArgumentException();
}
public final String getClazzType()
/*{ require { opcode == opc_new
|| opcode == opc_checkcast
|| opcode == opc_instanceof
|| opcode == opc_multianewarray
:: "Instruction has no typesig" } }*/
public String getClazzType()
{
return (String) objData;
throw new IllegalArgumentException();
}
public final void setClazzType(String type)
/*{ require { opcode == opc_new
|| opcode == opc_checkcast
|| opcode == opc_instanceof
|| opcode == opc_multianewarray
:: "Instruction has no typesig" } }*/
public void setClazzType(String type)
{
objData = type;
throw new IllegalArgumentException();
}
public final int[] getValues()
/*{ require { opcode == opc_lookupswitch
:: "Instruction has no values" } }*/
public int[] getValues()
{
return (int[]) objData;
throw new IllegalArgumentException();
}
public final void setValues(int[] values)
/*{ require { opcode == opc_lookupswitch
:: "Instruction has no values" } }*/
public void setValues(int[] values)
{
objData = values;
throw new IllegalArgumentException();
}
public final boolean doesAlwaysJump() {
switch (opcode) {
switch (getOpcode()) {
case opc_ret:
case opc_goto:
case opc_jsr:
case opc_tableswitch:
case opc_lookupswitch:
case opc_ireturn:
case opc_lreturn:
@ -299,351 +246,6 @@ public final class Instruction implements Opcodes{
}
}
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 -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; 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
@ -656,175 +258,24 @@ public final class Instruction implements Opcodes{
/*{ require { poppush != null && poppush.length == 2
:: "poppush must be an array of two ints" } } */
{
byte delta = (byte) stackDelta.charAt(opcode);
if (delta < 0x40) {
byte delta = (byte) stackDelta.charAt(getOpcode());
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 final String getDescription() {
return toString();
}
public String toString() {
return "" + addr + "_" + Integer.toHexString(hashCode());
return opcodeString[getOpcode()];
}
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
/**
* 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
* The string is created by scripts/createStackDelta.pl
*/
protected 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\000\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\000";
}

@ -1,29 +0,0 @@
/* LineNumber Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* A simple class containing the info of the LineNumberTable
*/
public class LineNumber {
public Instruction start;
public int linenr;
}

@ -18,13 +18,84 @@
*/
package jode.bytecode;
import jode.util.UnifyHash;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
/**
* A simple class containing the info of the LocalVariableTable
*/
public class LocalVariableInfo {
public Instruction start, end;
public String name, type;
public int slot;
public final class LocalVariableInfo {
private String name, type;
private int slot;
private static LocalVariableInfo anonymous[];
static {
grow(5);
}
private static final UnifyHash unifier = new UnifyHash();
private LocalVariableInfo(int slot) {
this.slot = slot;
}
private LocalVariableInfo(int slot, String name, String type) {
this.slot = slot;
this.name = name;
this.type = type;
}
private static void grow(int upper) {
LocalVariableInfo[] newAnon = new LocalVariableInfo[upper];
int start = 0;
if (anonymous != null) {
start = anonymous.length;
System.arraycopy(anonymous, 0, newAnon, 0, start);
}
anonymous = newAnon;
for (int i=start; i< upper; i++)
anonymous[i] = new LocalVariableInfo(i);
}
public static LocalVariableInfo getInfo(int slot) {
if (slot >= anonymous.length)
grow(Math.max(slot + 1, anonymous.length * 2));
return anonymous[slot];
}
public static LocalVariableInfo getInfo(int slot, String name, String type) {
if (name == null && type == null)
return getInfo(slot);
int hash = slot ^ name.hashCode() ^ type.hashCode();
Iterator iter = unifier.iterateHashCode(hash);
while (iter.hasNext()) {
LocalVariableInfo lvi = (LocalVariableInfo) iter.next();
if (lvi.slot == slot
&& lvi.name.equals(name)
&& lvi.type.equals(type))
return lvi;
}
LocalVariableInfo lvi = new LocalVariableInfo(slot, name, type);
unifier.put(hash, lvi);
return lvi;
}
public int getSlot() {
return slot;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public String toString() {
String result = ""+slot;
if (name != null)
result += " ["+name+","+type+"]";
return result;
}
}

@ -1,32 +1,31 @@
## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
-subdir=$(subdir) -dependdir=$(top_builddir) \
-classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \
BasicBlocks.java \
BasicBlockReader.java \
BasicBlockWriter.java \
BinaryInfo.java \
BytecodeInfo.java \
Block.java \
ClassFormatException.java \
ClassInfo.java \
ClassPath.java \
ConstantPool.java \
FieldInfo.java \
GrowableConstantPool.java \
Handler.java \
InnerClassInfo.java \
Instruction.java \
LineNumber.java \
LocalVariableInfo.java \
MethodInfo.java \
Opcodes.java \
Reference.java \
SearchPath.java \
TypeSignature.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class)

@ -22,39 +22,35 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
///#def COLLECTIONEXTRA java.lang
import java.lang.Comparable;
///#enddef
public class MethodInfo extends BinaryInfo {
ClassInfo clazzInfo;
public final class MethodInfo extends BinaryInfo implements Comparable {
int modifier;
String name;
String typeSig;
BytecodeInfo bytecode;
BasicBlocks basicblocks;
String[] exceptions;
boolean syntheticFlag;
boolean deprecatedFlag;
public MethodInfo(ClassInfo ci) {
clazzInfo = ci;
public MethodInfo() {
}
public MethodInfo(ClassInfo ci,
String name, String typeSig, int modifier) {
this.clazzInfo = ci;
public MethodInfo(String name, String typeSig, int modifier) {
this.name = name;
this.typeSig = typeSig;
this.modifier = modifier;
}
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("Code")) {
bytecode = new BytecodeInfo(this);
bytecode.read(cp, input);
} else if ((howMuch & KNOWNATTRIBS) != 0
void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input, int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL && name.equals("Code")) {
basicblocks = new BasicBlocks(this);
basicblocks.read(cp, input, howMuch);
} else if (howMuch >= ClassInfo.DECLARATIONS
&& name.equals("Exceptions")) {
int count = input.readUnsignedShort();
exceptions = new String[count];
@ -77,7 +73,7 @@ public class MethodInfo extends BinaryInfo {
super.readAttribute(name, length, cp, input, howMuch);
}
public void read(ConstantPool constantPool,
void read(ConstantPool constantPool,
DataInputStream input, int howMuch) throws IOException {
modifier = input.readUnsignedShort();
name = constantPool.getUTF8(input.readUnsignedShort());
@ -85,17 +81,17 @@ public class MethodInfo extends BinaryInfo {
readAttributes(constantPool, input, howMuch);
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
if (bytecode != null)
bytecode.reserveSmallConstants(gcp);
void reserveSmallConstants(GrowableConstantPool gcp) {
if (basicblocks != null)
basicblocks.reserveSmallConstants(gcp);
}
public void prepareWriting(GrowableConstantPool gcp) {
void prepareWriting(GrowableConstantPool gcp) {
gcp.putUTF8(name);
gcp.putUTF8(typeSig);
if (bytecode != null) {
if (basicblocks != null) {
gcp.putUTF8("Code");
bytecode.prepareWriting(gcp);
basicblocks.prepareWriting(gcp);
}
if (exceptions != null) {
gcp.putUTF8("Exceptions");
@ -109,9 +105,9 @@ public class MethodInfo extends BinaryInfo {
prepareAttributes(gcp);
}
protected int getKnownAttributeCount() {
int getKnownAttributeCount() {
int count = 0;
if (bytecode != null)
if (basicblocks != null)
count++;
if (exceptions != null)
count++;
@ -122,13 +118,12 @@ public class MethodInfo extends BinaryInfo {
return count;
}
public void writeKnownAttributes(GrowableConstantPool gcp,
void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
if (bytecode != null) {
if (basicblocks != null) {
output.writeShort(gcp.putUTF8("Code"));
output.writeInt(bytecode.getSize());
bytecode.write(gcp, output);
basicblocks.write(gcp, output);
}
if (exceptions != null) {
int count = exceptions.length;
@ -156,18 +151,9 @@ public class MethodInfo extends BinaryInfo {
writeAttributes(constantPool, output);
}
public void dropInfo(int howMuch) {
if ((howMuch & KNOWNATTRIBS) != 0) {
bytecode = null;
exceptions = null;
}
if (bytecode != null)
bytecode.dropInfo(howMuch);
super.dropInfo(howMuch);
}
public ClassInfo getClazzInfo() {
return clazzInfo;
public void dropBody() {
basicblocks = null;
super.dropAttributes();
}
public String getName() {
@ -182,6 +168,10 @@ public class MethodInfo extends BinaryInfo {
return modifier;
}
public boolean isConstructor() {
return name.charAt(0) == '<';
}
public boolean isStatic() {
return Modifier.isStatic(modifier);
}
@ -194,8 +184,8 @@ public class MethodInfo extends BinaryInfo {
return deprecatedFlag;
}
public BytecodeInfo getBytecode() {
return bytecode;
public BasicBlocks getBasicBlocks() {
return basicblocks;
}
public String[] getExceptions() {
@ -222,18 +212,51 @@ public class MethodInfo extends BinaryInfo {
deprecatedFlag = flag;
}
public void setBytecode(BytecodeInfo newBytecode) {
clazzInfo.loadInfo(KNOWNATTRIBS);
bytecode = newBytecode;
public void setBasicBlocks(BasicBlocks newBasicblocks) {
basicblocks = newBasicblocks;
}
public void setExceptions(String[] newExceptions) {
clazzInfo.loadInfo(KNOWNATTRIBS);
exceptions = newExceptions;
}
/**
* Compares two MethodInfo objects for method order. The method
* order is as follows: First the static class intializer followed
* by constructor with type signature sorted lexicographic. Then
* all other methods sorted lexicographically by name. If two
* methods have the same name, they are sorted by type signature.
*
* @return a positive number if this method follows the other in
* method order, a negative number if it preceeds the
* other, and 0 if they are equal.
* @exception ClassCastException if other is not a ClassInfo.
*/
public int compareTo(Object other) {
MethodInfo mi = (MethodInfo) other;
/* Normally constructors should automatically sort themself to
* the beginning, but if method name starts with a digit, the
* order would be destroyed.
*
* The JVM explicitly forbids methods starting with digits,
* nonetheless some obfuscators break this rule.
*
* But note that <clinit> comes lexicographically before <init>.
*/
if (name.charAt(0) != mi.name.charAt(0)) {
if (name.charAt(0) == '<')
return -1;
if (mi.name.charAt(0) == '<')
return 1;
}
int result = name.compareTo(mi.name);
if (result == 0)
result = typeSig.compareTo(mi.typeSig);
return result;
}
public String toString() {
return "Method "+Modifier.toString(modifier)+" "+
typeSig + " " + clazzInfo.getName() + "."+ name;
typeSig + " " + name;
}
}

@ -267,4 +267,14 @@ public interface Opcodes {
public final static String newArrayTypes = "ZCFDBSIJ";
public 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)
};
}

@ -19,7 +19,9 @@
package jode.bytecode;
import jode.util.UnifyHash;
import @COLLECTIONS@.Iterator;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
/**
* This class represents a field or method reference.
@ -67,6 +69,10 @@ public class Reference {
}
public String toString() {
return clazz + " " + name + " " + type;
String classStr = clazz;
if (clazz.startsWith("L"))
classStr = clazz.substring(1, clazz.length() - 1)
.replace('/', '.');
return classStr + "." + name + " " + type;
}
}

@ -0,0 +1,101 @@
/* ReferenceInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
/**
* This class represents an instruction that needs a reference, i.e.
* a method invocation or field access instruction.
*/
public final class ReferenceInstruction extends Instruction {
private Reference reference;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public ReferenceInstruction(int opcode, Reference reference, int lineNr)
{
super(opcode, lineNr);
if (opcode < opc_getstatic || opcode > opc_invokeinterface)
throw new IllegalArgumentException("Instruction has no reference");
this.reference = reference;
}
/**
* Creates a simple opcode, without any parameters.
*/
public ReferenceInstruction(int opcode, Reference ref) {
this(opcode, ref, -1);
}
public final Reference getReference()
{
return reference;
}
public final void setReference(Reference ref)
{
reference = ref;
}
/**
* 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" } } */
{
Reference ref = getReference();
String typeSig = ref.getType();
int opcode = getOpcode();
switch (opcode) {
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
poppush[0] = opcode != opc_invokestatic ? 1 : 0;
poppush[0] += TypeSignature.getArgumentSize(typeSig);
poppush[1] = TypeSignature.getReturnSize(typeSig);
break;
case opc_putfield:
case opc_putstatic:
poppush[1] = 0;
poppush[0] = TypeSignature.getTypeSize(typeSig);
if (opcode == opc_putfield)
poppush[0]++;
break;
case opc_getstatic:
case opc_getfield:
poppush[1] = TypeSignature.getTypeSize(typeSig);
poppush[0] = opcode == opc_getfield ? 1 : 0;
break;
}
}
public String toString() {
return super.toString()+' '+reference;
}
}

@ -1,576 +0,0 @@
/* SearchPath Copyright (C) 1998-1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import jode.GlobalOptions;
/**
* This class represents a path of multiple directories and/or zip files,
* where we can search for file names.
*
* @author Jochen Hoenicke
*/
public class SearchPath {
/**
* We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs.
*
* We currently allow both pathSeparatorChar and
* altPathSeparatorChar and decide if it is a protocol separator
* by context.
*/
public static final char altPathSeparatorChar = ',';
URL[] bases;
byte[][] urlzips;
File[] dirs;
ZipFile[] zips;
String[] zipDirs;
Hashtable[] zipEntries;
private static void addEntry(Hashtable entries, String name) {
String dir = "";
int pathsep = name.lastIndexOf("/");
if (pathsep != -1) {
dir = name.substring(0, pathsep);
name = name.substring(pathsep+1);
}
Vector dirContent = (Vector) entries.get(dir);
if (dirContent == null) {
dirContent = new Vector();
entries.put(dir, dirContent);
if (dir != "")
addEntry(entries, dir);
}
dirContent.addElement(name);
}
private void fillZipEntries(int nr) {
Enumeration zipEnum = zips[nr].entries();
zipEntries[nr] = new Hashtable();
while (zipEnum.hasMoreElements()) {
ZipEntry ze = (ZipEntry) zipEnum.nextElement();
String name = ze.getName();
// if (name.charAt(0) == '/')
// name = name.substring(1);
if (zipDirs[nr] != null) {
if (!name.startsWith(zipDirs[nr]))
continue;
name = name.substring(zipDirs[nr].length());
}
if (!ze.isDirectory() && name.endsWith(".class"))
addEntry(zipEntries[nr], name);
}
}
private void readURLZip(int nr, URLConnection conn) {
int length = conn.getContentLength();
if (length <= 0)
// Give a approximation if length is unknown
length = 10240;
else
// Increase the length by one, so we hopefully don't need
// to grow the array later (we need a little overshot to
// know when the end is reached).
length++;
urlzips[nr] = new byte[length];
try {
InputStream is = conn.getInputStream();
int pos = 0;
for (;;) {
// This is ugly, is.available() may return zero even
// if there are more bytes.
int avail = Math.max(is.available(), 1);
if (pos + is.available() > urlzips[nr].length) {
// grow the byte array.
byte[] newarr = new byte
[Math.max(2*urlzips[nr].length, pos + is.available())];
System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
urlzips[nr] = newarr;
}
int count = is.read(urlzips[nr], pos, urlzips[nr].length-pos);
if (count == -1)
break;
pos += count;
}
if (pos < urlzips[nr].length) {
// shrink the byte array again.
byte[] newarr = new byte[pos];
System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
urlzips[nr] = newarr;
}
} catch (IOException ex) {
GlobalOptions.err.println("IOException while reading "
+"remote zip file "+bases[nr]);
// disable entry
bases[nr] = null;
urlzips[nr] = null;
return;
}
try {
// fill entries into hash table
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(urlzips[nr]));
zipEntries[nr] = new Hashtable();
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
String name = ze.getName();
// if (name.charAt(0) == '/')
// name = name.substring(1);
if (zipDirs[nr] != null) {
if (!name.startsWith(zipDirs[nr]))
continue;
name = name.substring(zipDirs[nr].length());
}
if (!ze.isDirectory() && name.endsWith(".class"))
addEntry(zipEntries[nr], name);
zis.closeEntry();
}
zis.close();
} catch (IOException ex) {
GlobalOptions.err.println("Remote zip file "+bases[nr]
+" is corrupted.");
// disable entry
bases[nr] = null;
urlzips[nr] = null;
zipEntries[nr] = null;
return;
}
}
/**
* Creates a new search path for the given path.
* @param path The path where we should search for files. They
* should be separated by the system dependent pathSeparator. The
* entries may also be zip or jar files.
*/
public SearchPath(String path) {
// Calculate a good approximation (rounded upwards) of the tokens
// in this path.
int length = 1;
for (int index=path.indexOf(File.pathSeparatorChar);
index != -1; length++)
index = path.indexOf(File.pathSeparatorChar, index+1);
if (File.pathSeparatorChar != altPathSeparatorChar) {
for (int index=path.indexOf(altPathSeparatorChar);
index != -1; length++)
index = path.indexOf(altPathSeparatorChar, index+1);
}
bases = new URL[length];
urlzips = new byte[length][];
dirs = new File[length];
zips = new ZipFile[length];
zipEntries = new Hashtable[length];
zipDirs = new String[length];
int i = 0;
for (int ptr=0; ptr < path.length(); ptr++, i++) {
int next = ptr;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
int index = ptr;
colon_separator:
while (next > ptr
&& next < path.length()
&& path.charAt(next) == ':') {
// Check if this is a URL instead of a pathSeparator
// Since this is a while loop it allows nested urls like
// jar:ftp://ftp.foo.org/pub/foo.jar!/
while (index < next) {
char c = path.charAt(index);
// According to RFC 1738 letters, digits, '+', '-'
// and '.' are allowed SCHEMA characters. We
// disallow '.' because it is a good marker that
// the user has specified a filename instead of a
// URL.
if ((c < 'A' || c > 'Z')
&& (c < 'a' || c > 'z')
&& (c < '0' || c > '9')
&& "+-".indexOf(c) == -1) {
break colon_separator;
}
index++;
}
next++;
index++;
while (next < path.length()
&& path.charAt(next) != File.pathSeparatorChar
&& path.charAt(next) != altPathSeparatorChar)
next++;
}
String token = path.substring(ptr, next);
ptr = next;
boolean mustBeJar = false;
// We handle jar URL's ourself.
if (token.startsWith("jar:")) {
index = 0;
do {
index = token.indexOf('!', index);
} while (index != -1 && index != token.length()-1
&& token.charAt(index+1) != '/');
if (index == -1 || index == token.length()-1) {
GlobalOptions.err.println("Warning: Illegal jar url "
+ token + ".");
continue;
}
zipDirs[i] = token.substring(index+2);
if (!zipDirs[i].endsWith("/"))
zipDirs[i] = zipDirs[i] + "/";
token = token.substring(4, index);
mustBeJar = true;
}
index = token.indexOf(':');
if (index != -1 && index < token.length()-2
&& token.charAt(index+1) == '/'
&& token.charAt(index+2) == '/') {
// This looks like an URL.
try {
bases[i] = new URL(token);
try {
URLConnection connection = bases[i].openConnection();
if (mustBeJar
|| token.endsWith(".zip") || token.endsWith(".jar")
|| connection.getContentType().endsWith("/zip")) {
// This is a zip file. Read it into memory.
readURLZip(i, connection);
}
} catch (IOException ex) {
// ignore
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: Security exception while accessing "
+bases[i]+".");
}
} catch (MalformedURLException ex) {
/* disable entry */
bases[i] = null;
dirs[i] = null;
}
} else {
try {
dirs[i] = new File(token);
if (mustBeJar || !dirs[i].isDirectory()) {
try {
zips[i] = new ZipFile(dirs[i]);
} catch (java.io.IOException ex) {
/* disable this entry */
dirs[i] = null;
}
}
} catch (SecurityException ex) {
/* disable this entry */
GlobalOptions.err.println
("Warning: SecurityException while accessing "
+ token + ".");
dirs[i] = null;
}
}
}
}
public boolean exists(String filename) {
for (int i=0; i<dirs.length; i++) {
if (zipEntries[i] != null) {
if (zipEntries[i].get(filename) != null)
return true;
String dir = "";
String name = filename;
int index = filename.lastIndexOf('/');
if (index >= 0) {
dir = filename.substring(0, index);
name = filename.substring(index+1);
}
Vector directory = (Vector)zipEntries[i].get(dir);
if (directory != null && directory.contains(name))
return true;
continue;
}
if (bases[i] != null) {
try {
URL url = new URL(bases[i], filename);
URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
return true;
} catch (IOException ex) {
/* ignore */
}
continue;
}
if (dirs[i] == null)
continue;
if (zips[i] != null) {
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
ZipEntry ze = zips[i].getEntry(fullname);
if (ze != null)
return true;
} else {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
try {
File f = new File(dirs[i], filename);
if (f.exists())
return true;
} catch (SecurityException ex) {
/* ignore and take next element */
}
}
}
return false;
}
/**
* Searches for a file in the search path.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return An InputStream for the file.
*/
public InputStream getFile(String filename) throws IOException {
for (int i=0; i<dirs.length; i++) {
if (urlzips[i] != null) {
ZipInputStream zis = new ZipInputStream
(new ByteArrayInputStream(urlzips[i]));
ZipEntry ze;
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
while ((ze = zis.getNextEntry()) != null) {
if (ze.getName().equals(fullname)) {
///#ifdef JDK11
// The skip method in jdk1.1.7 ZipInputStream
// is buggy. We return a wrapper that fixes
// this.
return new FilterInputStream(zis) {
private byte[] tmpbuf = new byte[512];
public long skip(long n) throws IOException {
long skipped = 0;
while (n > 0) {
int count = read(tmpbuf, 0,
(int)Math.min(n, 512L));
if (count == -1)
return skipped;
skipped += count;
n -= count;
}
return skipped;
}
};
///#else
/// return zis;
///#endif
}
zis.closeEntry();
}
continue;
}
if (bases[i] != null) {
try {
URL url = new URL(bases[i], filename);
URLConnection conn = url.openConnection();
conn.setAllowUserInteraction(true);
return conn.getInputStream();
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+" while accessing "
+bases[i]+filename);
ex.printStackTrace(GlobalOptions.err);
/* ignore and take next element */
} catch (FileNotFoundException ex) {
/* ignore and take next element */
}
continue;
}
if (dirs[i] == null)
continue;
if (zips[i] != null) {
String fullname = zipDirs[i] != null
? zipDirs[i] + filename : filename;
ZipEntry ze = zips[i].getEntry(fullname);
if (ze != null)
return zips[i].getInputStream(ze);
} else {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
try {
File f = new File(dirs[i], filename);
if (f.exists())
return new FileInputStream(f);
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+" while accessing "
+dirs[i]+filename);
/* ignore and take next element */
}
}
}
throw new FileNotFoundException(filename);
}
/**
* Searches for a filename in the search path and tells if it is a
* directory.
* @param filename the filename. The path components should be separated
* by <code>/</code>.
* @return true, if filename exists and is a directory, false otherwise.
*/
public boolean isDirectory(String filename) {
for (int i=0; i<dirs.length; i++) {
if (dirs[i] == null)
continue;
if (zips[i] != null && zipEntries[i] == null)
fillZipEntries(i);
if (zipEntries[i] != null) {
if (zipEntries[i].containsKey(filename))
return true;
} else {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
try {
File f = new File(dirs[i], filename);
if (f.exists())
return f.isDirectory();
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+" while accessing "
+dirs[i]+filename);
}
}
}
return false;
}
/**
* Searches for all files in the given directory.
* @param dirName the directory name. The path components should
* be separated by <code>/</code>.
* @return An enumeration with all files/directories in the given
* directory. */
public Enumeration listFiles(final String dirName) {
return new Enumeration() {
int pathNr;
Enumeration zipEnum;
int fileNr;
String localDirName =
(java.io.File.separatorChar != '/')
? dirName.replace('/', java.io.File.separatorChar)
: dirName;
File currentDir;
String[] files;
public String findNextFile() {
while (true) {
if (zipEnum != null) {
while (zipEnum.hasMoreElements()) {
return (String) zipEnum.nextElement();
}
zipEnum = null;
}
if (files != null) {
while (fileNr < files.length) {
String name = files[fileNr++];
if (name.endsWith(".class")) {
return name;
} else if (name.indexOf(".") == -1) {
/* ignore directories containing a dot.
* they can't be a package directory.
*/
File f = new File(currentDir, name);
if (f.exists() && f.isDirectory())
return name;
}
}
files = null;
}
if (pathNr == dirs.length)
return null;
if (zips[pathNr] != null && zipEntries[pathNr] == null)
fillZipEntries(pathNr);
if (zipEntries[pathNr] != null) {
Vector entries =
(Vector) zipEntries[pathNr].get(dirName);
if (entries != null)
zipEnum = entries.elements();
} else if (dirs[pathNr] != null) {
try {
File f = new File(dirs[pathNr], localDirName);
if (f.exists() && f.isDirectory()) {
currentDir = f;
files = f.list();
}
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
+" while accessing "
+dirs[pathNr]+localDirName);
/* ignore and take next element */
}
}
pathNr++;
}
}
String nextName;
public boolean hasMoreElements() {
return (nextName != null
|| (nextName = findNextFile()) != null);
}
public Object nextElement() {
if (nextName == null)
return findNextFile();
else {
String result = nextName;
nextName = null;
return result;
}
}
};
}
}

@ -0,0 +1,94 @@
/* SlotInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. 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 class SlotInstruction extends Instruction {
private LocalVariableInfo lvi;
/**
*/
public SlotInstruction(int opcode, LocalVariableInfo lvi, int lineNr) {
super(opcode, lineNr);
if (opcode != opc_iinc && opcode != opc_ret
&& (opcode < opc_iload || opcode > opc_aload)
&& (opcode < opc_istore || opcode > opc_astore))
throw new IllegalArgumentException("Instruction has no slot");
this.lvi = lvi;
}
/**
*/
public SlotInstruction(int opcode, int slot, int lineNr) {
this(opcode, LocalVariableInfo.getInfo(slot), lineNr);
}
/**
*/
public SlotInstruction(int opcode, LocalVariableInfo lvi) {
this(opcode, lvi, -1);
}
/**
*/
public SlotInstruction(int opcode, int slot) {
this(opcode, LocalVariableInfo.getInfo(slot), -1);
}
public boolean isStore() {
int opcode = getOpcode();
return opcode >= opc_istore && opcode <= opc_astore;
}
public boolean hasLocal() {
return true;
}
public final int getLocalSlot()
{
return lvi.getSlot();
}
public final LocalVariableInfo getLocalInfo()
{
return lvi;
}
public final void setLocalInfo(LocalVariableInfo info)
{
this.lvi = info;
}
public final void setLocalSlot(int slot)
{
if (lvi.getName() == null)
this.lvi = LocalVariableInfo.getInfo(slot);
else
this.lvi = LocalVariableInfo.getInfo(slot,
lvi.getName(), lvi.getType());
}
public String toString() {
return super.toString()+' '+lvi;
}
}

@ -0,0 +1,67 @@
/* SwitchInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.bytecode;
import jode.util.StringQuoter;
/**
* This class represents an instruction in the byte code.
*
*/
public class SwitchInstruction extends Instruction {
/**
* The values for this switch.
*/
private int[] values;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public SwitchInstruction(int opcode, int[] values, int lineNr) {
super(opcode, lineNr);
if (opcode != opc_lookupswitch)
throw new IllegalArgumentException("Instruction is no switch");
this.values = values;
}
/**
* Creates a simple opcode, without any parameters.
*/
public SwitchInstruction(int opcode, int[] values) {
this(opcode, values, -1);
}
public final int[] getValues()
{
return values;
}
public final void setValues(int[] values)
{
this.values = values;
}
public String toString() {
StringBuffer sb = new StringBuffer(opcodeString[getOpcode()]);
for (int i=0; i< values.length; i++)
sb.append(' ').append(values[i]);
return sb.toString();
}
}

@ -0,0 +1,86 @@
/* TypeDimensionInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. 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 class TypeDimensionInstruction extends TypeInstruction {
/**
* The dimension of this multianewarray operation.
*/
private int dimension;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public TypeDimensionInstruction(int opcode, String type, int dimension,
int lineNr) {
super(opcode, type, lineNr);
if (opcode != opc_multianewarray)
throw new IllegalArgumentException("Instruction has no dimension");
this.dimension = dimension;
}
/**
* Creates a simple opcode, without any parameters.
*/
public TypeDimensionInstruction(int opcode, String type, int dimension) {
this(opcode, type, dimension, -1);
}
/**
* Get the dimensions for an opc_anewarray opcode.
*/
public final int getDimensions()
{
return dimension;
}
/**
* Get the dimensions for an opc_anewarray opcode.
*/
public final void setDimensions(int dim)
{
dimension = dim;
}
/**
* 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" } } */
{
poppush[0] = dimension;
poppush[1] = 1;
}
public String toString() {
return super.toString()+' '+dimension;
}
}

@ -0,0 +1,64 @@
/* TypeInstruction Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program; see the file COPYING. 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 class TypeInstruction extends Instruction {
/**
* The typesignature of the class/array.
*/
private String typeSig;
/**
* Standard constructor: creates an opcode with parameter and
* lineNr.
*/
public TypeInstruction(int opcode, String typeSig, int lineNr) {
super(opcode, lineNr);
if (opcode != opc_new && opcode != opc_checkcast
&& opcode != opc_instanceof && opcode != opc_multianewarray)
throw new IllegalArgumentException("Instruction has no typesig");
this.typeSig = typeSig;
}
/**
* Creates a simple opcode, without any parameters.
*/
public TypeInstruction(int opcode, String typeSig) {
this(opcode, typeSig, -1);
}
public final String getClazzType()
{
return typeSig;
}
public final void setClazzType(String type)
{
typeSig = type;
}
public String toString() {
return super.toString()+' '+typeSig;
}
}

@ -19,6 +19,7 @@
package jode.bytecode;
import jode.AssertError;
import jode.util.UnifyHash;
/**
* This class contains some static methods to handle type signatures.
@ -143,10 +144,10 @@ public class TypeSignature {
return typeSig.substring(1);
}
public static ClassInfo getClassInfo(String typeSig) {
public static ClassInfo getClassInfo(ClassPath classpath, String typeSig) {
if (typeSig.charAt(0) != 'L')
throw new IllegalArgumentException();
return ClassInfo.forName
return classpath.getClassInfo
(typeSig.substring(1, typeSig.length()-1).replace('/', '.'));
}

Loading…
Cancel
Save