- 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