- 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-85095c16579emaster
parent
9c9de3b561
commit
723088e8be
@ -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+"]"; |
||||
} |
||||
} |
@ -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
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); |
||||
} |
||||
} |
||||
|
@ -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)+"]"; |
||||
} |
||||
} |
||||
|
@ -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; |
||||
} |
||||
|
@ -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; |
||||
} |
||||
} |
Loading…
Reference in new issue