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 |
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 |
developement kit installed. If you want to run this program you only |
||||||
need the java runtime environment. Version 1.1 is quite old, I |
need the java runtime environment. Version 1.1 is quite old, I |
||||||
recommend using Java 2 (jdk1.2 or above). |
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 GNU standard for configuration |
|
||||||
and makefiles. To build and install do the following: |
This package was designed to use the ANT from the jakarta.apache.org |
||||||
|
tools. I assume you have installed it correctly. |
||||||
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 |
Take some time to edit config.props. There are a few options you need |
||||||
"aclocal; automake -a; autoconf". |
to take care of. (Unfortunately ant can't test for executables). |
||||||
|
|
||||||
1). You need a java development kit (at least version 1.1), some unix |
Now you are ready to invoke ant. There are many possible targets, here |
||||||
tools and some java packages. Make sure that you have all java |
are the most useful ones: |
||||||
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 |
all builds class files and documentation. |
||||||
1.1. These packages are accessible from the following urls: |
build builds class files only (autodetects java version). |
||||||
|
build-1.1 builds JDK1.1 class files. |
||||||
CYGWIN (unix tools for win95/NT): |
doc builds documentation. |
||||||
http://sourceware.cygnus.com/cygwin/ |
dist creates all release files. |
||||||
|
test does some self tests. You need to have junit installed for this. |
||||||
JDK 1.1: |
clean cleans everything that doesn't belong to the source distribution. |
||||||
http://java.sun.com/products/jdk/1.1/index.htm |
cvsclean cleans everything that doesn't belong into the cvs repository. |
||||||
|
|
||||||
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. |
|
||||||
|
@ -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) { |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue