Added JUnit Test cases. * build.xml: Big update. * net/sf/jode/bytecode/BasicBlock.java: (updateMaxStackLocals): new method to calculate maxStack and maxLocals. (setBlocks): fixed calculation of handlers, call updateMaxLocals. * net/sf/jode/bytecode/BasicBlockReader.java: (maxLocals, maxStack): new fields. (readCode): read maxStack/Locals into private fields. (convert): check that maxStack/Locals match what we calculate. * net/sf/jode/bytecode/BinaryInfo.java: (getKnownAttributeCount): renamed to... (getAttributeCount): ... this, and also count internal attributes. Made it protected. (readAttribute): made protected. (drop): made protected. (prepareAttributes): made protected. (writeKnownAttributes): removed. (writeAttributes): made protected, use getAttributeCount. Changed policy: it doesn't call writeKnownAttribute, but instead it expects sub classes to override this method. (getAttributeSize): made protected, subclasses should override it. Changed all subclasses to new policy. * net/sf/jode/bytecode/Block.java: (lineNr): Removed, it wasn't used. (pop,push): Removed, replaced by ... (maxpop,maxpush,delta): ... these, with slightly changed semantics. (stackHeight): New variable. (Block): Default Constructor doesn't initialize fields now. (getCatchers): Renamed to ... (getHandlers): ... this, changed all callers. (initCode): Calculate maxpop, maxpush, delta correctly. (getStackPopPush): Changed accordingly to new fields. (setCode): Removed debugging output for illegal contents. * net/sf/jode/bytecode/Classes.java: Reworked handling of inner classes. (innerClasses): Field mustn't be null anymore when loaded. (setName): Update class in classpath. * net/sf/jode/bytecode/ClassPath.java: (renameClassInfo): new function, should only used by ClassInfo. * net/sf/jode/bytecode/ConstantPool.java: made public. (getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index. (iterateClassNames): New method. * net/sf/jode/decompiler/Main.java: (decompileClass): Catch ClassFormatExceptions and decompile remaining classes. * net/sf/jode/obfuscator/ClassIdentifier.java: Updated handling of inner/extra classes to new ClassInfo behaviour. (initSuperClasses): Load DECLARATION of super classes. * net/sf/jode/obfuscator/PackageIdentifier.java: Replace deprecated methods of ClassInfo with corresponding classpath calls. (loadMatchingClasses): Initialize packages loaded on demand if we are initialize. * net/sf/jode/obfuscator/modules/ConstantAnalyzer.java: Now extends SimpleAnalyzer. (canonizeIfaceRef): Removed; it is now inherited. (canonizeRef): likewise. Big updates to handle jsr correctly. (handleOpcode): Moved method to BlockInfo. * net/sf/jode/obfuscator/modules/SimpleAnalyzer.java: (canonizeIfaceRef): New method, copied from ConstantAnalyzer. (canonizeRef): call canonizeIfaceRef for interfaces. * net/sf/jode/util/UnifyHash.java (iterateHashCode): iterator now supports remove(). (remove): New method. git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1337 379699f6-c40d-0410-875b-85095c16579emaster
parent
1ce57d3614
commit
4352b285ab
@ -1,45 +1,23 @@ |
||||
Before installing, make sure you have at least version 1.1 of the java |
||||
developement kit installed. If you want to run this program you only |
||||
need the java runtime environment. Version 1.1 is quite old, I |
||||
recommend using Java 2 (jdk1.2 or above). |
||||
|
||||
This package was designed to use the GNU standard for configuration |
||||
and makefiles. To build and install do the following: |
||||
|
||||
0). If you have downloaded the code from the CVS repository create |
||||
configure and Makefile.in with autoconf-2.13 and automake-1.4. Type |
||||
"aclocal; automake -a; autoconf". |
||||
|
||||
1). You need a java development kit (at least version 1.1), some unix |
||||
tools and some java packages. Make sure that you have all java |
||||
packages that are needed in your classpath. This are gnu.getopt, and |
||||
if you have JDK 1.1 you also need the collection classes and swing for |
||||
1.1. These packages are accessible from the following urls: |
||||
|
||||
CYGWIN (unix tools for win95/NT): |
||||
http://sourceware.cygnus.com/cygwin/ |
||||
|
||||
JDK 1.1: |
||||
http://java.sun.com/products/jdk/1.1/index.htm |
||||
|
||||
Collection classes and Swing for JDK 1.1: |
||||
http://java.sun.com/beans/infobus/#DOWNLOAD_COLLECTIONS |
||||
http://java.sun.com/products/jfc/index.html#download-swing |
||||
|
||||
JDK 1.2: |
||||
http://java.sun.com/products/jdk/1.2/index.html |
||||
|
||||
Getopt: |
||||
http://www.urbanophile.com/arenn/hacking/download.html#getopt |
||||
|
||||
2). Run the "configure" script to configure the package. There are |
||||
various options you might want to pass to configure to control how the |
||||
package is built. "configure --help" will give a complete list. |
||||
|
||||
Use the --with-java option to specify the install directory of the jdk. |
||||
|
||||
If you have jikes, you should specify it with --with-jikes. You can |
||||
give a path to the directory where it resides, otherwise it is |
||||
searched in the path. |
||||
|
||||
3). Type "make" to build the package. |
||||
recommend using Java 2 (jdk1.2 or above). You need perl if you want |
||||
to compile a 1.1 version. |
||||
|
||||
This package was designed to use the ANT from the jakarta.apache.org |
||||
tools. I assume you have installed it correctly. |
||||
|
||||
Take some time to edit config.props. There are a few options you need |
||||
to take care of. (Unfortunately ant can't test for executables). |
||||
|
||||
Now you are ready to invoke ant. There are many possible targets, here |
||||
are the most useful ones: |
||||
|
||||
all builds class files and documentation. |
||||
build builds class files only (autodetects java version). |
||||
build-1.1 builds JDK1.1 class files. |
||||
doc builds documentation. |
||||
dist creates all release files. |
||||
test does some self tests. You need to have junit installed for this. |
||||
clean cleans everything that doesn't belong to the source distribution. |
||||
cvsclean cleans everything that doesn't belong into the cvs repository. |
||||
|
@ -1,73 +0,0 @@ |
||||
/* Subroutine Copyright (C) 2000 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 net.sf.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
|
||||
|
||||
/** |
||||
* <p>Represents a <code>jsr</code>-Subroutine.</p> |
||||
* |
||||
* <p>In my representation a subroutine consists of all blocks from |
||||
* which the ret instruction is reachable. Every subroutine must have |
||||
* a reachable ret instruction, or the jsr is replaced by a simple goto. |
||||
* </p> |
||||
* |
||||
* @author Jochen Hoenicke |
||||
* @see net.sf.jode.bytecode.BasicBlocks |
||||
* @see net.sf.jode.bytecode.Block |
||||
*/ |
||||
public final class Subroutine { |
||||
/** |
||||
* Subroutines may be nested. This points to the outer subroutine |
||||
* or to null if this doesn't have an outer. |
||||
*/ |
||||
private Subroutine outer; |
||||
|
||||
/** |
||||
* Each subroutine has exactly one ret instruction, which is the |
||||
* last instruction in the retBlock. The local of the ret |
||||
* instruction must equal the local where the first instruction of |
||||
* the subroutine stores to. |
||||
*/ |
||||
private Block retBlock; |
||||
|
||||
/** |
||||
* The set of locals that are accessed inside this subroutine. |
||||
*/ |
||||
private BitSet accessedLocals; |
||||
|
||||
public Block getRetBlock() { |
||||
return retBlock; |
||||
} |
||||
|
||||
public Subroutine getOuter() { |
||||
return outer; |
||||
} |
||||
|
||||
public boolean isAccessed(int slot) { |
||||
return accessedLocals.get(slot); |
||||
} |
||||
} |
@ -1,228 +0,0 @@ |
||||
/* TransformSubroutine Copyright (C) 1999-2000 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 net.sf.jode.bytecode; |
||||
import java.util.BitSet; |
||||
|
||||
/** |
||||
* <p>This class contains some code to transform the subroutines of |
||||
* a method into a normal form. The normal form is as following.</p> |
||||
* |
||||
* <p>Each subroutine block, meaning a block where some jsr |
||||
* instructions may jump to, must store the return address in a local |
||||
* variable immediately. There must be exactly one block with the |
||||
* corresponding <code>opc_ret</code> instruction and this block must |
||||
* be reachable from all blocks that belong to this subroutine. </b> |
||||
* |
||||
* <p>The JVM spec allows a subroutine, to leave the return address on |
||||
* stack for a while, even longer than the subroutine actually exists. |
||||
* One can also pop that value instead of storing it in a local |
||||
* variable. And finally its possible to store it in a variable, but |
||||
* there exists no reachable <code>opc_ret</code> instruction that returns |
||||
* to that address.</p> |
||||
* |
||||
* <p>If the return address is not used by the subroutine, we simply |
||||
* replace the jsr by a jump and remove the pop/store instruction that |
||||
* popped the return address.</p> |
||||
* |
||||
* <p>If the return address is used, but not immediately stored, we simply |
||||
* move the corresponding astore to the start of the subroutine.</p> |
||||
* |
||||
* @see net.sf.jode.bytecode.Block |
||||
* @see net.sf.jode.bytecode.Instruction |
||||
*/ |
||||
public class TransformSubroutine implements Opcodes { |
||||
private final static int SUBSTATUS_SUBROUTINE = 1; |
||||
private final static int SUBSTATUS_REMOVEDSUB = 2; |
||||
private final static int SUBSTATUS_NOTSUB = 3; |
||||
private final static int SUBSTATUS_INPROGRESS = 4; |
||||
|
||||
BasicBlocks bb; |
||||
Blocks[] blocks; |
||||
byte[] substatus; |
||||
Subroutine[] subInfo; |
||||
|
||||
// {
|
||||
// for (int i=0; i < blocks.length; i++) {
|
||||
// Instructions[] instrs = blocks[i].getInstructions();
|
||||
// if (instrs[instrs.length-1].getOpcode() == opc_jsr) {
|
||||
// int srBlock = instrs.getSuccs()[0].getBlockNr();
|
||||
// if (substatus[srBlock] == 0)
|
||||
// analyzeSubroutine(srBlock);
|
||||
// if (substatus[srBlock] == SUBSTATUS_REMOVED) {
|
||||
// Instructions[] newInstrs
|
||||
// = new Instruction[instrs.length-1];
|
||||
// System.arraycopy(instrs, 0, newInstrs, 0,
|
||||
// newInstrs.length);
|
||||
// Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; };
|
||||
// blocks[i].setCode(newInstrs, newSuccs);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
class SubroutineInfo { |
||||
int retPosition; |
||||
BitSet accessedLocals; |
||||
SubroutineInfo outer; |
||||
|
||||
SubroutineInfo(int retPos, Bitset accLocals, SubroutineInfo outer) { |
||||
this.outer = outer; |
||||
this.retPosition = retPos; |
||||
this.accessedLocals = accLocals; |
||||
} |
||||
|
||||
boolean merge(int retPos, BitSet accLocals, SubroutineInfo outer) { |
||||
if ((retPos < 0 || this.retPosition < 0) |
||||
&& retPos != this.retPosition) |
||||
throw new |
||||
} |
||||
} |
||||
|
||||
public TransformSubroutine(BasicBlocks bb) |
||||
throws ClassFormatException |
||||
{ |
||||
if (bb.getStartBlock() == null) |
||||
return; |
||||
|
||||
blocks = bb.getBlocks(); |
||||
substatus = new byte[blocks.length]; |
||||
analyzeBlock(blockNr, SUBSTATUS_NOTSUB, null); |
||||
} |
||||
|
||||
public void analyzeBlock(int blockNr, int status, SubroutineInfo outer, |
||||
BitSet retsOnStack) { |
||||
Block block = blocks[blockNr]; |
||||
if (status == SUBSTATUS_INPROGRESS) { |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
public void analyzeBlock(int blockNr, int status, SubroutineInfo outer) { |
||||
substatus[blockNr] = status; |
||||
accessedLocals[blockNr] = accessed; |
||||
Stack todo = new Stack(); |
||||
|
||||
todo.add(new BlockInfo(startBlockNr, 0, null)); |
||||
while (!todo.isEmpty()) { |
||||
BlockInfo info = todo.pop(); |
||||
Block block = blocks[info.blockNr]; |
||||
Instruction[] instrs = block.getInstructions(); |
||||
Instruction[] newInstrs = null; |
||||
Block[] succs = block.getSuccessors(); |
||||
|
||||
|
||||
if (substatus[info.blockNr] |
||||
== SUBSTATUS_INPROGRESS) { |
||||
int retPosition = info.retPosition; |
||||
BitSet = |
||||
retPosition < 0 ? info.accessedLocals.clone() : null; |
||||
|
||||
for (int i = 0; i < instrs.length; i++) { |
||||
Instruction instr = instrs[i]; |
||||
if (instr instanceof SlotInstruction) { |
||||
if (instr.getOpcode() == opc_astore |
||||
&& retPosition == -1) { |
||||
|
||||
/* We found the store operation. At least |
||||
* a candidate, since there may not be a |
||||
* corresponding ret. |
||||
*/ |
||||
|
||||
retPosition = instr.getLocalSlot(); |
||||
accessedLocals = null; |
||||
/* remove store Instruction. |
||||
*/ |
||||
newInstrs = new Instruction[instrs.length - 1]; |
||||
System.arraycopy(instrs, 0, newInstrs, 0, i); |
||||
System.arraycopy(instrs, i+1, newInstrs, i, |
||||
newInstrs.length - i); |
||||
|
||||
} else { |
||||
if (retPosition < 0) { |
||||
accessedLocals.add(instr.getLocalSlot()); |
||||
switch (instr.getOpcode()) { |
||||
case opc_lload: |
||||
case opc_dload: |
||||
case opc_lstore: |
||||
case opc_dstore: |
||||
accessedLocals.add(instr.getLocalSlot()+1); |
||||
} |
||||
} |
||||
} |
||||
} else if (instr.getOpcode() == opc_pop |
||||
&& retPosition == -1) { |
||||
/* We spontanously left the subroutine by popping. |
||||
* Remove the pop Instruction. |
||||
*/ |
||||
newInstrs = new Instruction[instrs.length - 1]; |
||||
System.arraycopy(instrs, 0, newInstrs, 0, i); |
||||
System.arraycopy(instrs, i+1, newInstrs, i, |
||||
newInstrs.length - i); |
||||
substatus[info.blockNr] = SUBSTATUS_NOTSUB; |
||||
break; |
||||
} else if ((instr.getOpcode() == opc_pop2 && |
||||
(retPosition == -1 || retPosition == -2))) { |
||||
/* We spontanously left the subroutine by popping. |
||||
* Replace the pop2 with a pop. |
||||
*/ |
||||
newInstrs = new Instruction[instrs.length]; |
||||
System.arraycopy(instrs, 0, newInstrs, 0, |
||||
instrs.length); |
||||
newInstrs[i] = Instruction.forOpcode(opc_pop); |
||||
substatus[info.blockNr] = SUBSTATUS_NOTSUB; |
||||
break; |
||||
} else if (instr.getOpcode() == opc_jsr) { |
||||
/* A recursive subroutine (or have we already |
||||
* spontanously left this subroutine?) |
||||
*/ |
||||
int srBlock = instrs.getSuccs()[0].getBlockNr(); |
||||
if (substatus[srBlock] == 0) |
||||
analyzeSubroutine(srBlock); |
||||
if (substatus[srBlock] == SUBSTATUS_INPROGRESS) { |
||||
/* We spontanously left this subroutine! */ |
||||
if (retPosition < 0) |
||||
/* This can't happen in valid code. |
||||
*/ |
||||
throw new CodeFormatException |
||||
("Can't merge return instr on Stack."); |
||||
substatus[info.blockNr] = SUBSTATUS_NOTSUB; |
||||
} |
||||
leftSub = true; |
||||
break; |
||||
} else if (substatus[srBlock] == SUBSTATUS_REMOVED) { |
||||
Instructions[] newInstrs |
||||
= new Instruction[instrs.length-1]; |
||||
System.arraycopy(instrs, 0, newInstrs, 0, |
||||
newInstrs.length); |
||||
Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; }; |
||||
blocks[i].setCode(newInstrs, newSuccs); |
||||
} |
||||
} |
||||
} |
||||
if (!leftSub) { |
||||
|
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
Loading…
Reference in new issue