diff --git a/jode/AUTHORS b/jode/AUTHORS
index fc38b9f..0f30348 100644
--- a/jode/AUTHORS
+++ b/jode/AUTHORS
@@ -1 +1 @@
-Jochen Hoenicke
\ No newline at end of file
+Jochen Hoenicke
diff --git a/jode/ChangeLog b/jode/ChangeLog
index 3b32dbd..1e1ed4a 100644
--- a/jode/ChangeLog
+++ b/jode/ChangeLog
@@ -1,3 +1,23 @@
+2001-08-08 Jochen Hoenicke
+ More Documentation updates.
+ * build.xml: Release rules.
+ * scripts/jcpp.pl: Don't make backups of original.
+ * net/sf/jode/bytecode/BasicBlocks.java (setBlocks): Check that
+ successors are inside method.
+ * net/sf/jode/bytecode/Block.java (getStackHeight): New Method.
+ * net/sf/jode/bytecode/ClassPath.java (Location): public class to
+ model a component of the class path. Previously it was Path.
+ (ClassPath): New constructors added that take Location objects.
+ * net/sf/jode/bytecode/ConstantPool.java (getClassName): Cache
+ constants.
+ * net/sf/jode/bytecode/GrowableConstantPool.java: Made public.
+ (grow): Check that not too many constants are added.
+ (reserveLongConstants): Removed (not used).
+ (copyConstant): Removed (not used).
+ * net/sf/jode/jvm/NewObject.java: Made package protected.
+ * net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java:
+ Big updates (almost rewrote from scratch). Still doesn't compile.
+
2001-08-05 Jochen Hoenicke
Documentation updates (INSTALL, javadoc).
diff --git a/jode/build.xml b/jode/build.xml
index f15fa07..153a52e 100644
--- a/jode/build.xml
+++ b/jode/build.xml
@@ -25,17 +25,19 @@
-
+
+
-
-
-
+
+
+
+
@@ -55,8 +57,8 @@
-
-
+
+
@@ -135,8 +137,9 @@
-
-
+
+
+
@@ -145,16 +148,13 @@
destdir="${build}"
classpathref="project.classpath"
deprecation="on">
-
-
-
-
@@ -162,50 +162,78 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -231,7 +259,9 @@
sourcepath="${src}"
destdir="${api.doc}"
use="yes">
-
+
@@ -263,8 +293,7 @@
-
-
+
diff --git a/jode/config.props b/jode/config.props
index 150fd2a..a78fa1d 100644
--- a/jode/config.props
+++ b/jode/config.props
@@ -1,9 +1,11 @@
# Do you have online access for generating javadoc?
# If not, where are your local files.
javadoc.offline=false
+#javadoc.href=http://java.sun.com/products/jdk/1.2/docs/api/
javadoc.packagelistLoc=
-# javadoc.offline=true
-# javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api
+#javadoc.offline=true
+javadoc.href=file:/usr/doc/inet/java/jdk1.2/docs/api
+#javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api
# Is Perl installed on your system?
#
diff --git a/jode/scripts/jcpp.pl b/jode/scripts/jcpp.pl
index 7f886f8..91e7b7b 100755
--- a/jode/scripts/jcpp.pl
+++ b/jode/scripts/jcpp.pl
@@ -225,8 +225,7 @@ for (@files) {
if ($changes == 0) {
unlink "$file.tmp";
} else {
- (rename "$file", "$file.orig"
- and rename "$file.tmp", "$file")
+ (unlink "$file" and rename "$file.tmp", "$file")
or print STDERR "$file: Couldn't rename files.\n";
}
} else {
diff --git a/jode/src/net/sf/jode/bytecode/BasicBlocks.java b/jode/src/net/sf/jode/bytecode/BasicBlocks.java
index 8975096..ba12142 100644
--- a/jode/src/net/sf/jode/bytecode/BasicBlocks.java
+++ b/jode/src/net/sf/jode/bytecode/BasicBlocks.java
@@ -49,25 +49,43 @@ import java.lang.UnsupportedOperationException;
* Instead this information is stored inside the blocks. See
* Block for details.
*
- *
A subroutine block, i.e. a block where some jsr instructions may
- * jump to, must store its return address in a local variable
- * immediately. There must be exactly one block with the
- * corresponding opc_ret instruction and all blocks that
- * belong to this subroutine must point to the ret block. Bytecode
- * that doesn't have this condition is automatically transformed on
- * reading.
- *
*
Exception Handlers are represented by the Handler class. Their
* start/end range must span over some consecutive BasicBlocks and
* there handler must be another basic block.
*
- *
If you want to create or modify the byte code, you must first set
- * the basic blocks and then set exception handlers. If you set new
- * blocks the previous exception handlers will be removed.
+ *
*
*
When the code is written to a class file, the blocks are written
* in the given order. Goto and return instructions are inserted as
- * necessary, you don't have to care about that.
+ * necessary, you don't need to care about that.
+ *
+ *
Creating new BasicBlocks
+ *
+ *
If you want to create a new BasicBlocks object, first create the
+ * Block objects, then initialize them (you need to have all successor
+ * blocks created for this). Afterwards create a new BasicBlock and
+ * fill its sub blocks:
+ *
+ *
+ * MethodInfo myMethod = new MethodInfo("foo", "()V", PUBLIC);
+ * Block blocks = new Block[10];
+ * for (int i = 0; i < 10; i++) blocks[i] = new Block();
+ * blocks[0].setCode(new Instruction[] {...},
+ * new Block[] {blocks[3], blocks[1]});
+ * ...
+ * Handler[] excHandlers = new Handler[1];
+ * excHandlers[0] = new Handler(blocks[2], blocks[5], blocks[6],
+ * "java.lang.NullPointerException");
+ * BasicBlocks bb = new BasicBlocks(myMethod);
+ * bb.setCode(blocks, blocks[0], excHandlers);
+ * classInfo.setMethods(new MethodInfo[] { myMethod });
+ *
*
* @see net.sf.jode.bytecode.Block
* @see net.sf.jode.bytecode.Instruction
@@ -238,13 +256,13 @@ public class BasicBlocks extends BinaryInfo implements Opcodes {
public void setBlocks(Block[] blocks, Block startBlock,
Handler[] handlers) {
- for (int i = 0; i < blocks.length; i++)
- blocks[i].blockNr = i;
this.blocks = blocks;
this.startBlock = startBlock;
+
exceptionHandlers = handlers.length == 0 ? Handler.EMPTY : handlers;
ArrayList activeHandlers = new ArrayList();
for (int i = 0; i < blocks.length; i++) {
+ blocks[i].blockNr = i;
for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getStart() == blocks[i])
activeHandlers.add(handlers[j]);
@@ -259,6 +277,17 @@ public class BasicBlocks extends BinaryInfo implements Opcodes {
activeHandlers.remove(handlers[j]);
}
}
+ /* Check if all successor blocks are in this basic block */
+ for (int i = 0; i < blocks.length; i++) {
+ Block[] succs = blocks[i].getSuccs();
+ for (int j = 0; j < succs.length; j++) {
+ if (succs[j] != null
+ && succs[j] != blocks[succs[j].blockNr])
+ throw new IllegalArgumentException
+ ("Succ " + j + " of block " + i
+ + " not in basicblocks");
+ }
+ }
updateMaxStackLocals();
// TransformSubroutine.createSubroutineInfo(this);
}
diff --git a/jode/src/net/sf/jode/bytecode/BinaryInfo.java b/jode/src/net/sf/jode/bytecode/BinaryInfo.java
index 9ffa1fd..662b98d 100644
--- a/jode/src/net/sf/jode/bytecode/BinaryInfo.java
+++ b/jode/src/net/sf/jode/bytecode/BinaryInfo.java
@@ -42,7 +42,7 @@ import java.util.Iterator;
*
*
There are some predefined attributes, even the Code of a Method
* is an attribute. These predefined attributes are all handled by
- * this package as appropriate. This methods are only useful for non
+ * this package as appropriate. These methods are only useful for non
* standard attributes.
*
*
You can provide new attributes by overriding the protected
@@ -253,7 +253,7 @@ public class BinaryInfo {
* BasicBlocks.
*
* @return the total length of all attributes, including their
- * headers and the number of attributes field.
+ * headers and the "number of attributes" field.
*/
protected int getAttributeSize() {
int size = 2; /* attribute count */
@@ -267,8 +267,9 @@ public class BinaryInfo {
/**
* Finds a non standard attribute with the given name. You don't
- * have access to the constant pool. Instead extend this class
- * and override readAttribute method if you need the pool.
+ * have access to the constant pool. If you need the pool don't
+ * use this method but extend this class and override
+ * readAttribute method.
* @param name the name of the attribute.
* @return the contents of the attribute, null if not found.
* @see #readAttribute
@@ -282,12 +283,14 @@ public class BinaryInfo {
/**
* Gets all non standard attributes.
* @return an iterator for all attributes. The values returned by
- * the next() method of the iterator are of byte[] type.
+ * the next() method of the iterator are of Map.Entry type. The
+ * key of the entry is the name of the attribute, while the values
+ * are the byte[] contents.
* @see #findAttribute
*/
public Iterator getAttributes() {
if (unknownAttributes != null)
- return unknownAttributes.values().iterator();
+ return unknownAttributes.entrySet().iterator();
return Collections.EMPTY_SET.iterator();
}
diff --git a/jode/src/net/sf/jode/bytecode/Block.java b/jode/src/net/sf/jode/bytecode/Block.java
index 88de266..8ade2ac 100644
--- a/jode/src/net/sf/jode/bytecode/Block.java
+++ b/jode/src/net/sf/jode/bytecode/Block.java
@@ -232,6 +232,15 @@ public final class Block {
throw new IllegalArgumentException("no single successor block");
}
+ /**
+ * Returns the stack height at the beginning of the block. This
+ * is automatically calculated, when the block is inserted in a
+ * basic block.
+ */
+ public int getStackHeight () {
+ return stackHeight;
+ }
+
public void getStackPopPush (int[] poppush) {
poppush[0] = maxpop;
poppush[1] = delta + maxpop;
diff --git a/jode/src/net/sf/jode/bytecode/ClassInfo.java b/jode/src/net/sf/jode/bytecode/ClassInfo.java
index f3dd540..7cd368e 100644
--- a/jode/src/net/sf/jode/bytecode/ClassInfo.java
+++ b/jode/src/net/sf/jode/bytecode/ClassInfo.java
@@ -356,7 +356,11 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
ClassInfo(String name, ClassPath classpath) {
- this.name = name.intern();
+ /* Name may be null when reading class with unknown name from
+ * stream.
+ */
+ if (name != null)
+ this.name = name.intern();
this.classpath = classpath;
}
@@ -658,10 +662,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
/**
- * Reads a class file from a data input stream. You should really
+ * Reads a class file from a data input stream. Normally you should
* load a class from its classpath instead. This may
* be useful for special kinds of input streams, that ClassPath
- * doesn't handle though.
+ * doesn't handle.
*
* @param input The input stream, containing the class in standard
* bytecode format.
@@ -700,56 +704,43 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
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);
- int superID = input.readUnsignedShort();
- superclass = superID == 0 ? null
- : classpath.getClassInfo(cpool.getClassName(superID));
- int count = input.readUnsignedShort();
- interfaces = new ClassInfo[count];
- for (int i = 0; i < count; i++) {
- interfaces[i] = classpath.getClassInfo
- (cpool.getClassName(input.readUnsignedShort()));
- }
- }
+ /* modifiers */
+ modifiers = input.readUnsignedShort();
+ /* name */
+ String className = cpool.getClassName(input.readUnsignedShort());
+ if (name == null)
+ name = className;
+ else if (!name.equals(className))
+ throw new ClassFormatException("wrong name " + className);
+
+ /* superclass */
+ int superID = input.readUnsignedShort();
+ superclass = superID == 0 ? null
+ : classpath.getClassInfo(cpool.getClassName(superID));
+
+ /* interfaces */
+ int count = input.readUnsignedShort();
+ interfaces = new ClassInfo[count];
+ for (int i = 0; i < count; i++) {
+ interfaces[i] = classpath.getClassInfo
+ (cpool.getClassName(input.readUnsignedShort()));
+ }
/* fields */
- if (howMuch >= PUBLICDECLARATIONS) {
- int count = input.readUnsignedShort();
- fields = new FieldInfo[count];
- for (int i = 0; i < count; i++) {
- fields[i] = new FieldInfo();
- 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);
- }
- }
+ count = input.readUnsignedShort();
+ fields = new FieldInfo[count];
+ for (int i = 0; i < count; i++) {
+ fields[i] = new FieldInfo();
+ fields[i].read(cpool, input, howMuch);
+ }
/* methods */
- if (howMuch >= PUBLICDECLARATIONS) {
- int count = input.readUnsignedShort();
- methods = new MethodInfo[count];
- for (int i = 0; i < count; i++) {
- methods[i] = new MethodInfo();
- 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);
- }
- }
+ count = input.readUnsignedShort();
+ methods = new MethodInfo[count];
+ for (int i = 0; i < count; i++) {
+ methods[i] = new MethodInfo();
+ methods[i].read(cpool, input, howMuch);
+ }
/* initialize inner classes to empty array, in case there
* is no InnerClasses attribute.
@@ -769,9 +760,19 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (ci.status < OUTERCLASS)
ci.mergeOuterInfo(null, null, -1, false);
}
+
+ /* Set status */
status = howMuch;
}
+ /****** WRITING CLASS FILES ***************************************/
+
+ /**
+ * Reserves constant pool entries for String, Integer and Float
+ * constants needed by the bytecode. These constants should have
+ * small constant pool indices so that a ldc instead of a ldc_w
+ * bytecode can be used.
+ */
private void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i = 0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp);
@@ -780,8 +781,11 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
methods[i].reserveSmallConstants(gcp);
}
- /****** WRITING CLASS FILES ***************************************/
-
+ /**
+ * Reserves all constant pool entries needed by this class. This
+ * is necessary, because the constant pool is the first thing
+ * written to the class file.
+ */
private void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name);
gcp.putClassName(superclass.name);
@@ -828,6 +832,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp);
}
+ /**
+ * Count the attributes needed by the class.
+ */
protected int getAttributeCount() {
int count = super.getAttributeCount();
if (sourceFile != null)
@@ -837,6 +844,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return count;
}
+ /**
+ * Write the attributes needed by the class, namely SourceFile
+ * and InnerClasses attributes.
+ */
protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
@@ -958,7 +969,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
/**
- * Guess the contents of a class. It
+ * Guess the contents of a class. This is a last resort if the
+ * file can't be read by the class path. It generates outer class
+ * information based on the class name, assumes that the class
+ * extends java.lang.Object, implements no interfaces and has no
+ * fields, methods or inner classes.
+ *
* @param howMuch The amount of information that should be read, e.g.
* HIERARCHY.
* @see #OUTERCLASS
@@ -1018,7 +1034,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
/**
- * This is the counter part to load. It will drop all
+ * This is the counter part to load and guess. It will drop all
* informations bigger than "keep" and clean up the memory.
* @param keep tells how much info we should keep, can be
* NONE or anything that load accepts.
@@ -1217,8 +1233,8 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
/**
* Sets the name of this class info. Note that by changing the
* name you may overwrite an already loaded class. This can have
- * ugly effects, as that overwritten class may still exist, but
- * can't be loaded via the classPath.
+ * ugly effects, as references to that overwritten class may still
+ * exist.
*/
public void setName(String newName) {
/* The class name is used as index in the hash table. We have
@@ -1396,6 +1412,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* Checks if this class is a super class of child. This loads the
* complete hierarchy of child on demand and can throw an IOException
* if some classes are not found or broken.
+ *
+ * It doesn't check for cycles in class hierarchy, so it may get
+ * into an eternal loop.
+ *
* @param child the class that should be a child class of us.
* @return true if this is as super class of child, false otherwise
* @exception IOException if hierarchy of child could not be loaded.
@@ -1414,7 +1434,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* complete hierarchy of clazz on demand and can throw an IOException
* if some classes are not found or broken. If this class is not an
* interface it returns false, but you should check it yourself for
- * better performance.
+ * better performance.
+ *
+ * It doesn't check for cycles in class hierarchy, so it may get
+ * into an eternal loop.
* @param clazz the class to be checked.
* @return true if this is a interface and is implemented by clazz,
* false otherwise
diff --git a/jode/src/net/sf/jode/bytecode/ClassPath.java b/jode/src/net/sf/jode/bytecode/ClassPath.java
index b2f2af8..ded50ab 100644
--- a/jode/src/net/sf/jode/bytecode/ClassPath.java
+++ b/jode/src/net/sf/jode/bytecode/ClassPath.java
@@ -60,22 +60,27 @@ import net.sf.jode.util.UnifyHash;
*
A URL (unified resource location), pointing to a directory
*
A URL pointing to a jar or zip file.
*
A Jar URL (see {@link java.net.JarURLConnection}), useful if
- * the jar file is not packed correctly
- *
The reflection URL reflection:/ This location can
- * only load declarations of classes. If a security manager is
- * present, it can only load public declarations.
+ * the jar file is not packed correctly.
+ *
The reflection URL reflection:/. This is a
+ * special location, which fills the ClassInfo with the information
+ * from the java reflection API. Obviously it can't load any files
+ * nor the full bytecode. It only loads declarations of classes. If a
+ * security manager is present, it can only load public
+ * declarations.
*
*
- * We use standard java means to find a class file: package correspong
+ * We use standard java means to find a class file: package correspond
* to directories and the class file must have the .class
* extension. For example if the class path points to
* /home/java, the class java.lang.Object is
- * loaded from /home/java/java/lang/Object.class.
+ * loaded from /home/java/java/lang/Object.class. Of course
+ * you can write your own {@link Location}s that break this rule.
*
* 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.
+ * further fallback. The fallback is not used when listing classes or
+ * files.
*
* The main method for creating classes is {@link #getClassInfo}. The
* other available methods are useful to find other files in the
@@ -86,51 +91,133 @@ import net.sf.jode.util.UnifyHash;
* them.
*
* @author Jochen Hoenicke
- * @version 1.1 */
+ * @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 need a different pathSeparatorChar, since ':' (used for UNIX
+ * systems) is also used as 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
- * altPathSeparator, or the ClassPath(String[])
- * constructor.
+ * altPathSeparator, or better yet the
+ * ClassPath(String[]) or ClassPath(Location[]) constructors.
*/
public static final char altPathSeparatorChar = ',';
- private class Path {
- public boolean exists(String file) {
+ /**
+ * A location is a single component of the ClassPath. It provides
+ * methods to find files, list all files and reading them.
+ *
+ * Files and directories are always separated by "/" in this class,
+ * even under Windows where the default is a "\". This behaviour
+ * is consistent with that of {@link ZipFile}.
+ *
+ * You can extend this class to provide your own custom locations.
+ */
+ public static class Location {
+ /**
+ * Tells whether there exists a file or directory with
+ * the given name at this location.
+ * The default implementation returns false.
+ * @param file the name of the file, directories are always
+ * separated by "/".
+ * @return true if a file exists at this location.
+ */
+ protected boolean exists(String file) {
return false;
}
- public boolean isDirectory(String file) {
+
+ /**
+ * Tells whether there exists a directory (or package) with
+ * the given name at this location.
+ * The default implementation returns false.
+ * @param file the name of the directory, subdirectories are always
+ * separated by "/".
+ * @return true if a file exists at this location.
+ */
+ protected boolean isDirectory(String file) {
return false;
}
- public InputStream getFile(String file) throws IOException {
+
+ /**
+ * Returns an input stream that reads the given file. It is only
+ * called for files for which exists returns true.
+ * The default implementation returns null.
+ * @param file the name of the file, subdirectories are always
+ * separated by "/".
+ * @return an input stream for the given file, or null if file
+ * was not found.
+ * @exception IOException if an io exception occured while opening
+ * the file.
+ */
+ protected InputStream getFile(String file) throws IOException {
return null;
}
- public Enumeration listFiles(String directory) {
+
+ /**
+ * Lists the files and subdirectory in a directory. This is
+ * only called for directories for which isDirectory returns
+ * true.
+ *
+ * The objects returned by the nextElement()
+ * method of the Enumeration should be of type String and
+ * contain the file resp directory name without any parent
+ * directory names.
+ *
+ * Note that this method is also used by
+ * {@link ClassPath#listClassesAndPackages}.
+ *
+ * The default implementation returns null, which is equivalent
+ * to an empty enumeration.
+ *
+ * @param directory the name of the directory, subdirectories
+ * are always separated by "/".
+ * @return an enumeration, listing the file names. It may
+ * return null instead of an empty enumeration.
+ */
+ protected Enumeration listFiles(String directory) {
return null;
}
- public boolean loadClass(ClassInfo clazz, int howMuch)
+ /**
+ * Loads a class from this location and fills it with the given
+ * information.
+ * The default implementation will get the corresponding ".class"
+ * file via getFile() and fill the information from the stream.
+ * So normally there is no need to override this method.
+ *
+ *
+ * If you want to build classes on the fly, for example if you
+ * wrote a parser for java files and want to build class files
+ * from them, you can override this method.
+ *
+ * @param clazz the dot separated full qualified class name.
+ * @param howMuch the amount of information to load
+ * @return true, if loading the class was successful, false
+ * if it was not found.
+ * @exception ClassFormatException if class format is illegal
+ * @exception IOException if an io exception occured while reading
+ * the class.
+ * @see ClassInfo#read
+ */
+ protected 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)));
+ (new BufferedInputStream(getFile(file)));
clazz.read(input, howMuch);
return true;
}
}
- private class ReflectionPath extends Path {
- public boolean loadClass(ClassInfo classinfo, int howMuch)
+ private static class ReflectionLocation extends Location {
+ protected boolean loadClass(ClassInfo classinfo, int howMuch)
throws IOException, ClassFormatException
{
if (howMuch > ClassInfo.DECLARATIONS)
@@ -157,14 +244,14 @@ public class ClassPath {
}
}
- private class LocalPath extends Path {
+ private static class LocalLocation extends Location {
private File dir;
- public LocalPath(File path) {
+ protected LocalLocation(File path) {
dir = path;
}
- public boolean exists(String filename) {
+ protected boolean exists(String filename) {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
@@ -175,14 +262,14 @@ public class ClassPath {
}
}
- public boolean isDirectory(String filename) {
+ protected 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 {
+ protected InputStream getFile(String filename) throws IOException {
if (java.io.File.separatorChar != '/')
filename = filename
.replace('/', java.io.File.separatorChar);
@@ -190,7 +277,7 @@ public class ClassPath {
return new FileInputStream(f);
}
- public Enumeration listFiles(String directory) {
+ protected Enumeration listFiles(String directory) {
if (File.separatorChar != '/')
directory = directory
.replace('/', File.separatorChar);
@@ -222,7 +309,7 @@ public class ClassPath {
}
}
- private class ZipPath extends Path {
+ private static class ZipLocation extends Location {
private Hashtable entries = new Hashtable();
private ZipFile file;
private byte[] contents;
@@ -261,7 +348,7 @@ public class ClassPath {
} while (name.length() > 0);
}
- public ZipPath(ZipFile zipfile, String prefix) {
+ ZipLocation(ZipFile zipfile, String prefix) {
this.file = zipfile;
this.prefix = prefix;
@@ -272,7 +359,7 @@ public class ClassPath {
}
}
- public ZipPath(byte[] zipcontents, String prefix)
+ ZipLocation(byte[] zipcontents, String prefix)
throws IOException
{
this.contents = zipcontents;
@@ -290,7 +377,7 @@ public class ClassPath {
zis.close();
}
- public boolean exists(String filename) {
+ protected boolean exists(String filename) {
if (entries.containsKey(filename))
return true;
@@ -307,11 +394,11 @@ public class ClassPath {
return false;
}
- public boolean isDirectory(String filename) {
+ protected boolean isDirectory(String filename) {
return entries.containsKey(filename);
}
- public InputStream getFile(String filename) throws IOException {
+ protected InputStream getFile(String filename) throws IOException {
String fullname = prefix != null ? prefix + filename : filename;
if (contents != null) {
ZipInputStream zis = new ZipInputStream
@@ -353,7 +440,7 @@ public class ClassPath {
return null;
}
- public Enumeration listFiles(String directory) {
+ protected Enumeration listFiles(String directory) {
Vector direntries = (Vector) entries.get(directory);
if (direntries != null)
return direntries.elements();
@@ -365,14 +452,14 @@ public class ClassPath {
}
}
- private class URLPath extends Path {
+ private static class URLLocation extends Location {
private URL base;
- public URLPath(URL base) {
+ public URLLocation(URL base) {
this.base = base;
}
- public boolean exists(String filename) {
+ protected boolean exists(String filename) {
try {
URL url = new URL(base, filename);
URLConnection conn = url.openConnection();
@@ -384,7 +471,7 @@ public class ClassPath {
}
}
- public InputStream getFile(String filename) throws IOException {
+ protected InputStream getFile(String filename) throws IOException {
try {
URL url = new URL(base, filename);
URLConnection conn = url.openConnection();
@@ -395,9 +482,12 @@ public class ClassPath {
}
}
- public boolean loadClass(ClassInfo clazz, int howMuch)
+ protected boolean loadClass(ClassInfo clazz, int howMuch)
throws IOException, ClassFormatException
{
+ /* We override this method to avoid the costs of the
+ * exists call, and to optimize howMuch.
+ */
String file = clazz.getName().replace('.', '/') + ".class";
InputStream is = getFile(file);
if (is == null)
@@ -405,7 +495,11 @@ public class ClassPath {
DataInputStream input = new DataInputStream
(new BufferedInputStream(is));
- clazz.read(input, howMuch);
+
+ /* Reading an URL may be expensive. Therefore we ignore
+ * howMuch and read everything to avoid reading it again.
+ */
+ clazz.read(input, ClassInfo.ALL);
return true;
}
@@ -414,7 +508,7 @@ public class ClassPath {
}
}
- private Path[] paths;
+ private Location[] paths;
private UnifyHash classes = new UnifyHash();
ClassPath fallback = null;
@@ -422,7 +516,8 @@ public class ClassPath {
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
- * supported.
+ * supported. When a class or a file is not found in the class
+ * path the fallback is used.
* @param path An array of paths.
* @param fallback The fallback classpath.
*/
@@ -441,6 +536,25 @@ public class ClassPath {
initPath(paths);
}
+ /**
+ * Creates a new class path for the given path. When a class
+ * or a file is not found in the class path the fallback is used.
+ * @param locs An array of locations.
+ * @param fallback The fallback classpath.
+ */
+ public ClassPath(Location[] locs, ClassPath fallback) {
+ this.fallback = fallback;
+ paths = locs;
+ }
+
+ /**
+ * Creates a new class path for the given path.
+ * @param locs An array of locations.
+ */
+ public ClassPath(Location[] locs) {
+ paths = locs;
+ }
+
/**
* Creates a new class path for the given path. See the class
* description for more information, which kind of paths are
@@ -468,7 +582,66 @@ public class ClassPath {
initPath(tokenizeClassPath(path));
}
- private String[] tokenizeClassPath(String path) {
+ /**
+ * Creates a location for a given path component. See the
+ * class comment which path components are supported.
+ * @param path the path component.
+ * @return a location corresponding to the class.
+ * @exception NullPointerException if path is null.
+ * @exception IOException if an io exception occured while accessing the
+ * path component.
+ * @exception SecurityException if a security exception occured
+ * while accessing the path component.
+ */
+ public static Location createLocation(String path)
+ throws IOException, SecurityException
+ {
+ String zipPrefix = null;
+ // The special reflection URL
+ if (path.startsWith("reflection:"))
+ return new ReflectionLocation();
+
+ // We handle jar URL's ourself, this makes them work even with
+ // java 1.1
+ 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)
+ throw new MalformedURLException(path);
+ zipPrefix = path.substring(index+2);
+ if (!zipPrefix.endsWith("/"))
+ zipPrefix += "/";
+ path = path.substring(4, index);
+ }
+
+ int index = path.indexOf(':');
+ /* Grrr, we need to distinguish c:\foo from URLs. */
+ if (index > 1) {
+ // This looks like an URL.
+ URL base = new URL(path);
+ 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);
+ return new ZipLocation(contents, zipPrefix);
+ } else
+ return new URLLocation(base);
+ } else {
+ File dir = new File(path);
+ if (zipPrefix != null || !dir.isDirectory()) {
+ return new ZipLocation(new ZipFile(dir), zipPrefix);
+ } else
+ return new LocalLocation(dir);
+ }
+ }
+
+ private static String[] tokenizeClassPath(String path) {
// Calculate a good approximation (rounded upwards) of the tokens
// in this path.
int length = 1;
@@ -528,133 +701,66 @@ public class ClassPath {
return tokens;
}
- private byte[] readURLZip(URLConnection conn) {
+ private static byte[] readURLZip(URLConnection conn) throws IOException {
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
+ // to grow the array later (we need one byte 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];
+ 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 + avail > contents.length) {
+ // grow the byte array.
+ byte[] newarr = new byte
+ [Math.max(2*contents.length, pos + avail)];
System.arraycopy(contents, 0, newarr, 0, pos);
contents = newarr;
}
- return contents;
- } catch (IOException ex) {
- return null;
+ 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;
}
private void initPath(String[] tokens) {
int length = tokens.length;
- paths = new Path[length];
+ paths = new Location[length];
for (int i = 0; i < length; i++) {
- String path = tokens[i];
- if (path == null)
+ if (tokens[i] == 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 + ".");
- }
+ try {
+ paths[i] = createLocation(tokens[i]);
+ } catch (MalformedURLException ex) {
+ GlobalOptions.err.println
+ ("Warning: Malformed URL "+ tokens[i] + ".");
+ } catch (IOException ex) {
+ GlobalOptions.err.println
+ ("Warning: IO exception while accessing "
+ +tokens[i]+".");
+ } catch (SecurityException ex) {
+ GlobalOptions.err.println
+ ("Warning: Security exception while accessing "
+ +tokens[i]+".");
}
}
}
@@ -663,10 +769,17 @@ public class ClassPath {
/**
* 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.
+ * ClassInfo.loadInfo. It is no error if class doesn't exists.
+ *
+ * ClassInfos are guaranteed to be unique, i.e. if you have two
+ * ClsssInfo objects loaded from the same class path with the same
+ * classname they will always be identical. The only exception is
+ * if you use setName() or getClassInfoFromStream() and explicitly
+ * overwrite a previous class.
+ *
* @param classname the dot-separated full qualified name of the class.
* For inner classes you must use the bytecode name with $,
- * e.g. java.util.Map$Entry.
+ * e.g. java.util.Map$Entry.
* @exception IllegalArgumentException if class name isn't valid.
*/
public ClassInfo getClassInfo(String classname)
@@ -684,6 +797,44 @@ public class ClassPath {
return clazz;
}
+ /**
+ * Creates a new class info from an input stream containing the
+ * bytecode. This method is useful if you don't know the class
+ * name or if you have the class in an unusual location. The
+ * class is fully loaded ({@link ClassInfo#ALL}) when you use this
+ * method.
+ *
+ * If a class with the same name was already created with
+ * getClassInfo() it will effectively be removed from the class
+ * path, although references to it may still exists elsewhere.
+ *
+ * @param stream the input stream containing the bytecode.
+ * @return the ClassInfo representing this class.
+ * @exception IOException if an io exception occurs.
+ * @exception ClassFormatException if bytecode isn't valid.
+ */
+ public ClassInfo getClassInfoFromStream(InputStream stream)
+ throws IOException, ClassFormatException
+ {
+ ClassInfo classInfo = new ClassInfo(null, this);
+ classInfo.read(new DataInputStream(new BufferedInputStream(stream)),
+ ClassInfo.ALL);
+ String classname = classInfo.getName();
+ /* Remove the classinfo with the same name from this path if
+ * it exists.
+ */
+ Iterator iter = classes.iterateHashCode(classname.hashCode());
+ while (iter.hasNext()) {
+ ClassInfo clazz = (ClassInfo) iter.next();
+ if (clazz.getName().equals(classname)) {
+ iter.remove();
+ break;
+ }
+ }
+ classes.put(classname.hashCode(), classInfo);
+ return classInfo;
+ }
+
/**
* Updates the classes unify hash for a class renaming. This
* should be only called by {@link ClassInfo#setName}.
@@ -740,7 +891,7 @@ public class ClassPath {
/**
* Searches for a file in the class path.
* @param filename the filename. The path components should be separated
- * by /.
+ * by "/".
* @return An InputStream for the file.
*/
public InputStream getFile(String filename) throws IOException {
@@ -748,6 +899,8 @@ public class ClassPath {
if (paths[i] != null && paths[i].exists(filename))
return paths[i].getFile(filename);
}
+ if (fallback != null)
+ return fallback.getFile(filename);
throw new FileNotFoundException(filename);
}
@@ -755,7 +908,7 @@ public class ClassPath {
* 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 /.
+ * by "/".
* @return true, if filename exists and is a directory, false otherwise.
*/
public boolean isDirectory(String filename) {
@@ -768,7 +921,8 @@ public class ClassPath {
/**
* Searches for a filename in the class path and tells if it is a
- * package. This is the same as isDirectory.
+ * package. This is the same as isDirectory, but takes dot separated
+ * names.
* @param fqn the full qualified name. The components should be dot
* separated.
* @return true, if filename exists and is a package, false otherwise.
@@ -781,7 +935,7 @@ public class ClassPath {
/**
* Get a list of all files in a given directory.
* @param dirName the directory name. The path components must
- * be separated by /.
+ * be separated by "/".
* @return An enumeration with all files/directories in the given
* directory. If dirName doesn't denote a directory it returns null.
*/
@@ -855,6 +1009,9 @@ public class ClassPath {
};
}
+ /**
+ * Loads the contents of a class. This is only called by ClassInfo.
+ */
boolean loadClass(ClassInfo clazz, int howMuch)
throws IOException, ClassFormatException
{
@@ -867,13 +1024,17 @@ public class ClassPath {
return false;
}
+ /**
+ * Returns a string representation of this classpath.
+ * @return a string useful for debugging purposes.
+ */
public String toString() {
StringBuffer sb = new StringBuffer("ClassPath[");
for (int i = 0; i < paths.length; i++) {
if (paths[i] != null)
sb.append(paths[i]).append(',');
}
- sb.append(fallback).append(']');
+ sb.append("fallback=").append(fallback).append(']');
return sb.toString();
}
}
diff --git a/jode/src/net/sf/jode/bytecode/ConstantPool.java b/jode/src/net/sf/jode/bytecode/ConstantPool.java
index f90b7ce..e73dabd 100644
--- a/jode/src/net/sf/jode/bytecode/ConstantPool.java
+++ b/jode/src/net/sf/jode/bytecode/ConstantPool.java
@@ -30,9 +30,10 @@ import java.lang.UnsupportedOperationException;
///#enddef
/**
- * This class represent the constant pool. You normally don't need to
- * access this class, except if you want to add your own custom
- * attributes that use the constant pool.
+ * This class represent the constant pool. Normally you wont need to
+ * touch this class, as ClassInfo already does all the hard work. You
+ * will only need it if you want to add your own custom attributes
+ * that use the constant pool.
*
* @author Jochen Hoenicke
*/
@@ -179,13 +180,16 @@ public class ConstantPool {
public String getClassName(int i) throws ClassFormatException {
if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch");
- String clName = getUTF8(indices1[i]);
- try {
- TypeSignature.checkTypeSig("L"+clName+";");
- } catch (IllegalArgumentException ex) {
- throw new ClassFormatException(ex.getMessage());
+ if (constants[i] == null) {
+ String clName = getUTF8(indices1[i]);
+ try {
+ TypeSignature.checkTypeSig("L"+clName+";");
+ } catch (IllegalArgumentException ex) {
+ throw new ClassFormatException(ex.getMessage());
+ }
+ constants[i] = clName.replace('/','.').intern();
}
- return clName.replace('/','.').intern();
+ return (String) constants[i];
}
/**
diff --git a/jode/src/net/sf/jode/bytecode/FieldInfo.java b/jode/src/net/sf/jode/bytecode/FieldInfo.java
index a257d00..c810ae7 100644
--- a/jode/src/net/sf/jode/bytecode/FieldInfo.java
+++ b/jode/src/net/sf/jode/bytecode/FieldInfo.java
@@ -26,6 +26,39 @@ import java.lang.reflect.Modifier;
import java.lang.Comparable;
///#enddef
+/**
+ * Represents a java bytecode field (class variable). A field
+ * consists of the following parts:
+ *
+ *
+ *
+ *
name
The field's name
+ *
+ *
type
The field's {@link TypeSignature type signature}
+ * in bytecode format.
+ *
+ *
modifiers
The modifiers of the field like private, public etc.
+ * These are created by or-ing the constants {@link Modifier#PUBLIC},
+ * {@link Modifier#PRIVATE}, {@link Modifier#PROTECTED},
+ * {@link Modifier#STATIC}, {@link Modifier#FINAL},
+ * {@link Modifier#VOLATILE}, {@link Modifier#TRANSIENT},
+ * {@link Modifier#STRICT}
+ * of class {@link java.lang.reflect.Modifier}.
+ *
+ *
synthetic
true if this field is synthetic.
+ *
+ *
deprecated
true if this field is deprecated.
+ *
+ *
constant
Final static fields may have a constant
+ * value. This is either of type String, Integer, Long, Float or
+ * Double.
+ *
+ *
+ *
+ * @author Jochen Hoenicke
+ * @see net.sf.jode.bytecode.TypeSignature
+ * @see net.sf.jode.bytecode.BasicBlocks
+ */
public final class FieldInfo extends BinaryInfo implements Comparable {
int modifier;
String name;
@@ -34,10 +67,21 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
Object constant;
boolean syntheticFlag;
boolean deprecatedFlag;
-
+
+ /**
+ * Creates a new empty field info.
+ */
public FieldInfo() {
}
+ /**
+ * Creates a new field with given name, type and modifiers.
+ * @param name the name of the field.
+ * @param typeSig the typeSig the type signature.
+ * @param modifier the modifier
+ * @see TypeSignature
+ * @see Modifier
+ */
public FieldInfo(String name, String typeSig, int modifier) {
this.name = name;
this.typeSig = typeSig;
@@ -134,7 +178,7 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
}
void write(GrowableConstantPool constantPool,
- DataOutputStream output) throws IOException {
+ DataOutputStream output) throws IOException {
output.writeShort(modifier);
output.writeShort(constantPool.putUTF8(name));
output.writeShort(constantPool.putUTF8(typeSig));
@@ -147,38 +191,81 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
super.drop(keep);
}
+ /**
+ * Gets the name of the field.
+ * @return the name.
+ */
public String getName() {
return name;
}
+ /**
+ * Gets the type signature of the field.
+ * @return the type signature.
+ * @see TypeSignature
+ */
public String getType() {
return typeSig;
}
+ /**
+ * Gets the modifier of the field.
+ * @return the modifiers.
+ * @see Modifier
+ */
public int getModifiers() {
return modifier;
}
+ /**
+ * Tells whether this field is synthetic.
+ * @return true if the field is synthetic.
+ */
public boolean isSynthetic() {
return syntheticFlag;
}
+ /**
+ * Tells whether this field is deprecated.
+ * @return true if the field is deprecated.
+ */
public boolean isDeprecated() {
return deprecatedFlag;
}
+ /**
+ * Gets the constant value of the field. For static final fields
+ * that have a simple String, int, float, double or long constant,
+ * this returns the corresponding constant as String, Integer, Float
+ * Double or long. For other fields it returns null.
+ * @return The constant, or null.
+ */
public Object getConstant() {
return constant;
}
+ /**
+ * Sets the name of the field.
+ * @param newName the name.
+ */
public void setName(String newName) {
name = newName;
}
+ /**
+ * Sets the type signature of the field.
+ * @param newType the type signature.
+ * @see TypeSignature
+ */
public void setType(String newType) {
typeSig = newType;
}
+ /**
+ * Sets the modifier of the field.
+ * @param newModifier the modifiers.
+ * @see Modifier
+ */
public void setModifiers(int newModifier) {
modifier = newModifier;
}
diff --git a/jode/src/net/sf/jode/bytecode/GrowableConstantPool.java b/jode/src/net/sf/jode/bytecode/GrowableConstantPool.java
index 56432f5..88e7ab4 100644
--- a/jode/src/net/sf/jode/bytecode/GrowableConstantPool.java
+++ b/jode/src/net/sf/jode/bytecode/GrowableConstantPool.java
@@ -23,11 +23,14 @@ import java.io.IOException;
import java.util.Hashtable;
/**
- * This class represent a constant pool, where new constants can be added to.
+ * This class represent a constant pool, where new constants can be
+ * added to. Normally you wont need to touch this class, as ClassInfo
+ * already does all the hard work. You will only need it if you want
+ * to add your own custom attributes that use the constant pool.
*
* @author Jochen Hoenicke
*/
-class GrowableConstantPool extends ConstantPool {
+public class GrowableConstantPool extends ConstantPool {
Hashtable entryToIndex = new Hashtable();
boolean written;
@@ -59,6 +62,9 @@ class GrowableConstantPool extends ConstantPool {
}
}
+ /**
+ * Create a new growable constant pool
+ */
public GrowableConstantPool () {
count = 1;
tags = new int[128];
@@ -68,11 +74,13 @@ class GrowableConstantPool extends ConstantPool {
written = false;
}
- public final void grow(int wantedSize) {
+ private final void grow(int wantedSize) {
if (written)
throw new IllegalStateException("adding to written ConstantPool");
+ if (wantedSize > 65535)
+ throw new IllegalArgumentException("Too many constants added");
if (tags.length < wantedSize) {
- int newSize = Math.max(tags.length*2, wantedSize);
+ int newSize = Math.min(65535, Math.max(tags.length*2, wantedSize));
int[] tmpints = new int[newSize];
System.arraycopy(tags, 0, tmpints, 0, count);
tags = tmpints;
@@ -117,7 +125,7 @@ class GrowableConstantPool extends ConstantPool {
return newIndex;
}
- int putIndexed(int tag, Object obj1, int index1, int index2) {
+ private int putIndexed(int tag, Object obj1, int index1, int index2) {
Key key = new Key(tag, obj1, index2);
Integer indexObj = (Integer) entryToIndex.get(key);
if (indexObj != null) {
@@ -135,16 +143,45 @@ class GrowableConstantPool extends ConstantPool {
return count++;
}
+ /**
+ * Adds a new UTF8 entry to the constant pool and returns the index.
+ * If it already exists it will reuse the previous entry.
+ * @param utf the UTF8 string.
+ * @return the index of the pool entry.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
+ */
public final int putUTF8(String utf) {
return putConstant(UTF8, utf);
}
+ /**
+ * Adds a new class name entry to the constant pool and returns
+ * the index. If it already exists it will reuse the previous
+ * entry.
+ * @param name the dot separated full qualified class name.
+ * @return the index of the pool entry.
+ * @exception IllegalArgumentException if the class name is illegal.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
+ */
public int putClassName(String name) {
name = name.replace('.','/');
TypeSignature.checkTypeSig("L"+name+";");
return putIndexed(CLASS, name, putUTF8(name), 0);
}
+ /**
+ * Adds a new class entry to the constant pool and returns
+ * the index. If it already exists it will reuse the previous
+ * entry. This is the same as putClassName, except for the format
+ * of the parameter and that it can also handle arrays.
+ * @param name the type signature of the class to add.
+ * @return the index of the pool entry.
+ * @exception IllegalArgumentException if the class name is illegal.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
+ */
public int putClassType(String name) {
TypeSignature.checkTypeSig(name);
if (name.charAt(0) == 'L')
@@ -154,6 +191,19 @@ class GrowableConstantPool extends ConstantPool {
return putIndexed(CLASS, name, putUTF8(name), 0);
}
+ /**
+ * Adds a new field/method or interface reference to the constant
+ * pool and returns the index. If it already exists it will reuse
+ * the previous entry.
+ * @param tag the tag of the reference, one of FIELDREF, METHODREF
+ * or INTERFACEMETHODREF.
+ * @param ref the reference.
+ * @return the index of the pool entry.
+ * @exception IllegalArgumentException if the reference type or
+ * class name is illegal.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
+ */
public int putRef(int tag, Reference ref) {
String className = ref.getClazz();
String typeSig = ref.getType();
@@ -172,10 +222,14 @@ class GrowableConstantPool extends ConstantPool {
}
/**
- * Puts a constant into this constant pool
- * @param c the constant, must be of type
- * Integer, Long, Float, Double or String
- * @return the index into the pool of this constant.
+ * Puts a "one slot" constant into this constant pool. If it
+ * already exists it will reuse the previous entry.
+ * @param c the constant; must be of type
+ * Integer, Float or String
+ * @return the index of the pool entry.
+ * @exception IllegalArgumentException if the constant is of wrong type.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
*/
public int putConstant(Object c) {
if (c instanceof String) {
@@ -194,10 +248,13 @@ class GrowableConstantPool extends ConstantPool {
}
/**
- * Puts a constant into this constant pool
- * @param c the constant, must be of type
- * Integer, Long, Float, Double or String
- * @return the index into the pool of this constant.
+ * Puts a "double slot" constant into this constant pool. If it
+ * already exists it will reuse the previous entry.
+ * @param c the constant; must be of type Long or Double
+ * @return the index of the pool entry.
+ * @exception IllegalArgumentException if the constant is of wrong type.
+ * @exception IllegalStateException if the pool was already written,
+ * but the entry was not added before.
*/
public int putLongConstant(Object c) {
int tag;
@@ -212,9 +269,11 @@ class GrowableConstantPool extends ConstantPool {
}
/**
- * Reserve an entry in this constant pool for a constant (for ldc).
+ * Reserves an entry in this constant pool for a constant (for ldc).
+ * The constant must still be put into the pool, before the pool may
+ * be written.
* @param c the constant, must be of type
- * Integer, Long, Float, Double or String
+ * Integer, Float or String
* @return the reserved index into the pool of this constant.
*/
public int reserveConstant(Object c) {
@@ -226,25 +285,14 @@ class GrowableConstantPool extends ConstantPool {
}
/**
- * Reserve an entry in this constant pool for a constant (for ldc).
- * @param c the constant, must be of type
- * Integer, Long, Float, Double or String
- * @return the reserved index into the pool of this constant.
+ * Writes the constant pool to the stream. This is normally called
+ * by {@link ClassInfo#write}.
+ * @param stream the stream to write to.
+ * @exception IOException if it occured while writing.
*/
- public int reserveLongConstant(Object c) {
- return putLongConstant(c);
- }
-
- public int copyConstant(ConstantPool cp, int index)
- throws ClassFormatException {
- return putConstant(cp.getConstant(index));
- }
-
public void write(DataOutputStream stream)
throws IOException {
written = true;
- if (count > 65536)
- throw new ClassFormatError("Too many constants");
stream.writeShort(count);
for (int i=1; i< count; i++) {
int tag = tags[i];
diff --git a/jode/src/net/sf/jode/bytecode/Instruction.java b/jode/src/net/sf/jode/bytecode/Instruction.java
index 7063205..9e2db65 100644
--- a/jode/src/net/sf/jode/bytecode/Instruction.java
+++ b/jode/src/net/sf/jode/bytecode/Instruction.java
@@ -20,11 +20,13 @@
package net.sf.jode.bytecode;
/**
- * This class represents an instruction in the byte code.
+ *
This class represents an instruction in the byte code.
+ * Instructions can be created with the static {@link #forOpcode}
+ * methods.
*
- * We only allow a subset of opcodes. Other opcodes are mapped to
- * their simpler version. When writing the bytecode the shortest
- * possible bytecode is produced.
+ *
We only allow a subset of opcodes. Other opcodes are mapped to
+ * their simpler version. Don't worry about this, when writing the
+ * bytecode the shortest possible bytecode is produced.
*
* The opcodes we map are:
*
@@ -216,55 +218,104 @@ public class Instruction implements Opcodes{
}
/**
- * Returns the opcode of the instruction.
+ * Gets the opcode of the instruction.
+ * @return the opcode of the instruction.
*/
public final int getOpcode() {
return lineAndOpcode & 0xff;
}
+ /**
+ * Tells whether there is a line number information for this
+ * instruction.
+ * @return true if there is a line number information for this
+ * instruction.
+ */
public final boolean hasLineNr() {
return lineAndOpcode >= 0;
}
+ /**
+ * Gets the line number of this instruction.
+ * @return the line number, or -1 if there isn't one.
+ */
public final int getLineNr() {
return lineAndOpcode >> 8;
}
+ /**
+ * Sets the line number of this instruction.
+ * @param nr the line number; use -1 to clear it.
+ */
public final void setLineNr(int nr) {
lineAndOpcode = (nr << 8) | (lineAndOpcode & 0xff);
}
+ /**
+ * Checks whether this instruction is a local store instruction, i.e.
+ * one of astore, istore, lstore,
+ * fstore or dstore.
+ */
public boolean isStore() {
return false;
}
+ /**
+ * Checks whether this instruction accesses a local slot.
+ */
public boolean hasLocal() {
return false;
}
+ /**
+ * Gets the slot number of the local this instruction accesses.
+ * @return the slot number.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * access a local slot.
+ */
public int getLocalSlot()
{
throw new IllegalArgumentException();
// UnsupportedOperationException would be more appropriate
}
+ /**
+ * Gets the information of the local this instruction accesses.
+ * @return the local variable info.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * access a local.
+ */
public LocalVariableInfo getLocalInfo()
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the information of the local this instruction accesses.
+ * @param info the local variable info.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * access a local.
+ */
public void setLocalInfo(LocalVariableInfo info)
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the slot of the local this instruction accesses.
+ * @param slot the local slot
+ * @throws IllegalArgumentException if this instruction doesn't
+ * access a local.
+ */
public void setLocalSlot(int slot)
{
throw new IllegalArgumentException();
}
/**
- * Get the increment for an opc_iinc instruction.
+ * Gets the increment for an opc_iinc instruction.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
*/
public int getIncrement()
{
@@ -272,7 +323,9 @@ public class Instruction implements Opcodes{
}
/**
- * Set the increment for an opc_iinc instruction.
+ * Sets the increment for an opc_iinc instruction.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
*/
public void setIncrement(int incr)
{
@@ -280,7 +333,9 @@ public class Instruction implements Opcodes{
}
/**
- * Get the dimensions for an opc_anewarray opcode.
+ * Gets the dimensions for an opc_multianewarray opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
*/
public int getDimensions()
{
@@ -288,53 +343,101 @@ public class Instruction implements Opcodes{
}
/**
- * Set the dimensions for an opc_anewarray opcode.
+ * Sets the dimensions for an opc_multianewarray opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
*/
public void setDimensions(int dims)
{
throw new IllegalArgumentException();
}
+ /**
+ * Gets the constant for a opc_ldc or opc_ldc2_w opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public Object getConstant()
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the constant for a opc_ldc or opc_ldc2_w opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public void setConstant(Object constant)
{
throw new IllegalArgumentException();
}
+ /**
+ * Gets the reference of the field or method this instruction accesses.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public Reference getReference()
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the reference of the field or method this instruction accesses.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public void setReference(Reference ref)
{
throw new IllegalArgumentException();
}
+ /**
+ * Gets the class type this instruction uses, e.g if its a class cast.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public String getClazzType()
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the class type this instruction uses, e.g if its a class cast.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public void setClazzType(String type)
{
throw new IllegalArgumentException();
}
+ /**
+ * Gets the values of a opc_lookupswitch opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public int[] getValues()
{
throw new IllegalArgumentException();
}
+ /**
+ * Sets the values of a opc_lookupswitch opcode.
+ * @throws IllegalArgumentException if this instruction doesn't
+ * support this.
+ */
public void setValues(int[] values)
{
throw new IllegalArgumentException();
}
+ /**
+ * Checks whether this instruction always changes program flow.
+ * Returns false for opc_jsr it.
+ * @return true if this instruction always changes flow, i.e. if
+ * its an unconditional jump, a return, a throw, a ret or a switch.
+ */
public final boolean doesAlwaysJump() {
switch (getOpcode()) {
case opc_ret:
@@ -380,6 +483,12 @@ public class Instruction implements Opcodes{
return toString();
}
+ /**
+ * Gets a printable representation of the opcode with its
+ * parameters. This will not include the destination for jump
+ * instructions, since this information is not stored inside the
+ * instruction.
+ */
public String toString() {
return opcodeString[getOpcode()];
}
diff --git a/jode/src/net/sf/jode/bytecode/LocalVariableInfo.java b/jode/src/net/sf/jode/bytecode/LocalVariableInfo.java
index f2cdeaa..622c2ad 100644
--- a/jode/src/net/sf/jode/bytecode/LocalVariableInfo.java
+++ b/jode/src/net/sf/jode/bytecode/LocalVariableInfo.java
@@ -24,7 +24,17 @@ import java.util.Iterator;
///#enddef
/**
- * A simple class containing the info of the LocalVariableTable
+ * A simple class containing the info of the LocalVariableTable. This
+ * info is stored decentral: every load, store or iinc instruction contains
+ * the info for its local. When writing code it will automatically be
+ * collected again.
+ *
+ * You can't modify a LocalVariableInfo, for this reason they can and
+ * will be shared.
+ *
+ * This information consists of name, type signature and slot number.
+ * There is no public constructor; use the static getInfo() methods
+ * instead.
*/
public final class LocalVariableInfo {
private String name, type;
@@ -56,14 +66,25 @@ public final class LocalVariableInfo {
for (int i=start; i< upper; i++)
anonymous[i] = new LocalVariableInfo(i);
}
-
+
+ /**
+ * Creates a new local variable info, with no name or type.
+ * @param slot the slot number.
+ */
public static LocalVariableInfo getInfo(int slot) {
if (slot >= anonymous.length)
grow(Math.max(slot + 1, anonymous.length * 2));
return anonymous[slot];
}
- public static LocalVariableInfo getInfo(int slot, String name, String type) {
+ /**
+ * Creates a new local variable info, with given name and type.
+ * @param slot the slot number.
+ * @param name the name of the local.
+ * @param type the type signature of the local.
+ */
+ public static LocalVariableInfo getInfo(int slot,
+ String name, String type) {
if (name == null && type == null)
return getInfo(slot);
int hash = slot ^ name.hashCode() ^ type.hashCode();
@@ -80,18 +101,31 @@ public final class LocalVariableInfo {
return lvi;
}
+ /**
+ * Gets the slot number.
+ */
public int getSlot() {
return slot;
}
+ /**
+ * Gets the name.
+ */
public String getName() {
return name;
}
+ /**
+ * Gets the type signature.
+ * @see TypeSignature
+ */
public String getType() {
return type;
}
+ /**
+ * Gets a string representation for debugging purposes.
+ */
public String toString() {
String result = ""+slot;
if (name != null)
diff --git a/jode/src/net/sf/jode/bytecode/MethodInfo.java b/jode/src/net/sf/jode/bytecode/MethodInfo.java
index ec5ec0f..8356a11 100644
--- a/jode/src/net/sf/jode/bytecode/MethodInfo.java
+++ b/jode/src/net/sf/jode/bytecode/MethodInfo.java
@@ -27,8 +27,8 @@ import java.lang.Comparable;
///#enddef
/**
- *
Represents a java bytecode method. A method consists of the following
- * parts:
+ * Represents a java bytecode method. A method consists of the following
+ * parts:
*
*
The method's {@link TypeSignature type signature}
* in bytecode format.
*
- *
modifiers
The modifiers of the method like private, public etc.
- * These are created by or-ing the constants defined in
- * {@link java.lang.reflect.Modifier}.
+ *
modifiers
The modifiers of the field like private, public etc.
+ * These are created by or-ing the constants {@link Modifier#PUBLIC},
+ * {@link Modifier#PRIVATE}, {@link Modifier#PROTECTED},
+ * {@link Modifier#STATIC}, {@link Modifier#FINAL},
+ * {@link Modifier#SYNCHRONIZED}, {@link Modifier#NATIVE},
+ * {@link Modifier#ABSTRACT}, {@link Modifier#STRICT}
+ * of class {@link java.lang.reflect.Modifier}.
*
*
basicblocks
the bytecode of the method in form of
* {@link BasicBlocks basic blocks}, null if it is native or
diff --git a/jode/src/net/sf/jode/expr/InvokeOperator.java b/jode/src/net/sf/jode/expr/InvokeOperator.java
index d49f377..9b7efa3 100644
--- a/jode/src/net/sf/jode/expr/InvokeOperator.java
+++ b/jode/src/net/sf/jode/expr/InvokeOperator.java
@@ -82,6 +82,10 @@ public final class InvokeOperator extends Operator
* first element is the hint type of the return value, the
* remaining entries are the hint types of the parameters. All
* hint types may be null, if that parameter shouldn't be hinted.
+ *
+ * The reason why we don't put the class name into the top level
+ * key, is that we don't necessarily know the class. We may have
+ * a sub class, but the hint should of course still apply.
*/
private final static HashMap hintTypes = new HashMap();
@@ -100,7 +104,7 @@ public final class InvokeOperator extends Operator
* make much sense to hint for byte, since its constant
* representation is more difficult than an int
* representation. If you have more hints to suggest, please
- * write contact me. (see GlobalOptions.EMAIL)
+ * contact me. (see GlobalOptions.EMAIL)
*/
Type tCharHint = new IntegerType(IntegerType.IT_I, IntegerType.IT_C);
Type[] hintC = new Type[] { tCharHint };
@@ -1208,7 +1212,7 @@ public final class InvokeOperator extends Operator
}
writer.endOp();
- /* No the easier part: Dump the arguments from arg to length.
+ /* Now the easier part: Dump the arguments from arg to length.
* We still need to check for casts though.
*/
writer.breakOp();
diff --git a/jode/src/net/sf/jode/jvm/Interpreter.java b/jode/src/net/sf/jode/jvm/Interpreter.java
index 0959a74..abb6f24 100644
--- a/jode/src/net/sf/jode/jvm/Interpreter.java
+++ b/jode/src/net/sf/jode/jvm/Interpreter.java
@@ -36,8 +36,8 @@ import java.util.Iterator;
/**
* This class is a java virtual machine written in java :-). Well not
- * exactly. It does only handle a subset of the opcodes and is mainly
- * written do deobfuscate Strings.
+ * exactly. It is only a bytecode interpreter, you have to supply the
+ * rest of the VM (the runtime environment).
*
* @author Jochen Hoenicke
*/
diff --git a/jode/src/net/sf/jode/jvm/NewObject.java b/jode/src/net/sf/jode/jvm/NewObject.java
index dddedab..0f2b0b3 100644
--- a/jode/src/net/sf/jode/jvm/NewObject.java
+++ b/jode/src/net/sf/jode/jvm/NewObject.java
@@ -24,7 +24,7 @@ package net.sf.jode.jvm;
*
* @author Jochen Hoenicke
*/
-public class NewObject {
+class NewObject {
Object instance;
String type;
diff --git a/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java b/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java
index 7c8414f..cce0092 100644
--- a/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java
+++ b/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java
@@ -34,8 +34,10 @@ import java.lang.reflect.InvocationTargetException;
*
array of primitive type is mapped to itself (not array of Integer)
*
array of other types are mapped to array of mapped other type
*
- *
- * @author Jochen Hoenicke */
+ *
+ * @author Jochen Hoenicke
+ * @see SimpleRuntimeEnvironment
+ */
public interface RuntimeEnvironment {
/**
* Get the value of a field member.
diff --git a/jode/src/net/sf/jode/jvm/SimpleRuntimeEnvironment.java b/jode/src/net/sf/jode/jvm/SimpleRuntimeEnvironment.java
index 8ca526f..1b37d94 100644
--- a/jode/src/net/sf/jode/jvm/SimpleRuntimeEnvironment.java
+++ b/jode/src/net/sf/jode/jvm/SimpleRuntimeEnvironment.java
@@ -27,6 +27,11 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
+/**
+ * This is a runtime environment using reflection.
+ * Monitors are not supported by this class so exit/enterMonitor
+ * will through an InterpreterException.
+ */
public class SimpleRuntimeEnvironment implements RuntimeEnvironment {
public static Object fromReflectType(String typeSig, Object value) {
diff --git a/jode/src/net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java b/jode/src/net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java
index 82915e2..364ac2b 100644
--- a/jode/src/net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java
+++ b/jode/src/net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java
@@ -65,191 +65,481 @@ public class RemovePopAnalyzer implements CodeTransformer, Opcodes {
public BlockInfo analyzeBlock(Block block, BlockInfo oldInfo) {
}
- public BlockInfo[] calcBlockInfos(BasicBlocks bb) {
- Block[] blocks = bb.getBlocks();
- BlockInfo[] infos = new BlockInfo[blocks.length];
- int poppush[] = new int[2];
- int maxStack = bb.getMaxStack();
- int poppedLen = maxStack >> 5;
- for (int i = 0; i < blocks.length; i++) {
- BitSet popped = new BitSet();
- Instruction[] instrs = blocks[i].getInstructions();
- int[] removed = instrs.length >> 5;
-
- // Be conservative with stack depth at end of block
- int depth = maxStack;
-
- for (int j = instrs.length; j-- > 0; ) {
- Instruction instr = instrs[j];
-
- instr.getStackPopPush(poppush);
-
- // Now check if the parameters of this instr are needed.
- boolean params_needed = false;
- switch (instr.getOpcode()) {
- case opc_invokevirtual:
- case opc_invokespecial:
- case opc_invokestatic:
- case opc_invokeinterface:
- case opc_checkcast:
- /* These instructions have side effects, parameters
- * are always needed. Adjust depth.
- */
- params_needed = true;
- depth += poppush[1];
- break;
-
- default:
- /* Check if all results are needed, adjust depth */
- for (int k = 0; k < poppush[1]; k++) {
- if (!popped.get(depth++))
- params_needed = true;
- }
- }
+ /**
+ * This method propagates pops through a dup instruction, eventually
+ * generating new code and a new set of popped instructions.
+ *
+ * @param opcode the opcode of the dup instruction.
+ * @param newInstruction a list where the new instructions should
+ * be added to the front.
+ * @param stackDepth the stack depth after the dup is executed.
+ * @param poppedEntries the stack slots that should be popped at
+ * the end of this dup.
+ * @return The stack slots that should be popped at the start of
+ * the dup.
+ */
+ byte movePopsThroughDup(int opcode,
+ List newInstructions,
+ int poppedEntries) {
+ int count = (opcode - opc_dup)/3+1;
+ int depth = (opcode - opc_dup)%3;
+
+ /* Calculate which entries can be popped before this instruction,
+ * and update opcode.
+ */
+ int newPopped =
+ (((poppedEntries + 1) << depth) - 1) & (poppedEntries >> count);
+ int mask = ((1 << count) - 1);
+ int poppedDeep = poppedEntries & mask;
+ int poppedTop = (poppedEntries >> (depth + count)) & mask;
+ boolean swapAfterDup = false;
+ boolean swapBeforeDup = false;
+
+ for (int i = count+depth; i > depth; i--) {
+ if ((newPopped & (1 << i)) != 0)
+ depth--;
+ }
- if (params_needed) {
- /* mark params as needed */
- for (int k = 0; k < poppush[0]; k++)
- popped.clear(--depth);
- } else {
- removed[j >> 5] |= 1 << (j & 31);
- /* mark params as popped */
- for (int k = 0; k < poppush[0]; k++)
- popped.set(--depth);
- }
+ // adjust the depth
+ for (int i = depth; i > 0; i--) {
+ if ((newPopped & (1 << i)) != 0)
+ depth--;
+ }
+
+ // adjust the count and poppedDeep/3
+ if ((poppedDeep & poppedTop & 1) != 0) {
+ count--;
+ poppedDeep >>= 1;
+ poppedTop >>= 1;
+ mask >>= 1;
+ } else if ((poppedDeep & poppedTop & 2) != 0) {
+ count--;
+ poppedDeep &= 1;
+ poppedTop &= 1;
+ mask &= 1;
+ }
+
+ if (poppedDeep == mask
+ || (depth == 0 && poppedTop == mask)) {
+ // dup was not necessary
+ return newPopped;
+ }
+
+ /* Now (poppedDeep & poppedTop) == 0 */
+
+ if (poppedTop > 0) {
+ /* Insert the pop for the top elements, we add
+ * the dup later in front of these instructions.
+ */
+ if (poppedTop == 3) {
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop2));
+ } else {
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop));
+ if (count == 2 && poppedTop == 1)
+ swapAfterDup = true;
}
-
- int[] poppedArr = new int[poppedLen];
- if (blocks[i] != bb.getStartBlock()) {
- /* Only can pop entries before this block if
- * this isn't the start block.
+ }
+
+ if (poppedDeep != 0) {
+ if (poppedDeep == 2) {
+ /* We swap before and after dupping to get to
+ * poppedDeep = 1 case.
*/
- for (int k = 0; k < block_pop; k++) {
- if (popped.get(depth+k))
- poppedArr[k >> 5] |= 1 << (k & 31);
- }
+ swapAfterDup = !swapAfterDup;
+ swapBeforeDup = true;
}
- infos[i] = new BlockInfo(poppedArr, removed);
+ /* The bottom most value is popped; decrease count
+ * and increase depth, so that it won't be created
+ * in the first place.
+ */
+ depth++;
+ count--;
}
+
+ /* Now all pops are resolved */
+ /* Do a dup with count and depth now. */
+
+ if (swapAfterDup)
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_swap));
- /* Now start sharing poppedEntries as necessary. */
- int[] empty = new int[poppedLen];
- next_block:
- for (int i = 1; i < blocks.length; i++) {
- /* Try to find an earlier block with a same predecessor. */
- for (int j = 0; j < blocks.length; j++) {
- Block[] succs = blocks[j].getSuccs();
- if (succs.length < 2)
- continue;
-
- int blockNr = -1;
- boolean isAPred = false;
- for (int k = 0; k < succs.length; k++) {
- if (succs[k] == blocks[i])
- isAPred = true;
- if (succs[k] != null && succs[k].getBlockNr() < i)
- blockNr = succs[k].getBlockNr();
- if (isAPred && blockNr >= 0) {
- int[] common = infos[blockNr].poppedEntries;
- int[] my = infos[i].poppedEntries;
- for (int k = 0; k < poppedLen; k++)
- common[k] &= my[k];
- infos[i].poppedEntries = common;
- continue next_block;
- }
- }
- }
+ if (depth < 3) {
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop - 3 +
+ depth + 3 * count));
+ } else {
+ // I hope that this will almost never happen.
+ // depth = 3, count = 1;
+ // Note that instructions are backwards.
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop2)); //DABCD<
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_dup2_x2)); //DABCDAB<
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop)); //DCDAB<
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_dup_x2)); //DCDABD<
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_pop)); //DCABD<
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_dup2_x2)); //DCABDC<
+ swappedBeforeDup = !swappedBeforeDup //ABDC<
}
- /* Now propagate poppedEntries through blocks */
- boolean changed = true;
- while (changed) {
- changed = false;
- next_block:
- for (int i = 0; i < blocks.length; i++) {
- Block[] succs = blocks[i].getSuccs();
- int[] succPops = null;
- for (int j = 0; j < succs.length; j++) {
- if (succs[j] != null) {
- succPops = infos[succs[j].getBlockNr()].poppedEntries;
+ if (swapBeforeDup)
+ newInstructions.addFirst
+ (Instruction.forOpcode(opc_swap));
+ return newPopped
+ }
+
+ /**
+ * This method analyzes a block from end to start and removes the
+ * pop instructions together with their pushes. It propagates pops
+ * to the front removing various instructions on the fly, which may
+ * generate new pops for their operands and so on.
+ *
+ * @param block the block of code.
+ * @param poppedEntries the stack slots that should be popped at
+ * the end of this block.
+ * @return the stack slots that should be popped at the start of
+ * the block.
+ */
+ BitSet movePopsToFront(Block block, BitSet poppedEntries) {
+ /* Calculate stack height at end of block. */
+ Instruction[] oldInstrs = block.getInstructions();
+ LinkedList newInstructions = new LinkedList();
+
+ int instrNr = oldInstrs.length;
+ int stackDepth = block.getStackHeight() + block.getStackDelta();
+ while (instrNr > 0) {
+ Instruction instr = oldInstrs[--instrNr];
+ if (instr.getOpcode() == opc_nop)
+ continue;
+ if (instr.getOpcode() == opc_pop) {
+ popsAtEnd.set(stackDepth++);
+ continue;
+ }
+ if (instr.getOpcode() == opc_pop2) {
+ popsAtEnd.set(stackDepth++);
+ popsAtEnd.set(stackDepth++);
+ continue;
+ }
+
+ instr.getStackPopPush(poppush);
+
+ /* Check if this instr pushes a popped Entry. */
+ boolean push_a_popped = false;
+ boolean push_all_popped = true;
+ for (int j=0; j < poppush[1]; j++) {
+ if (poppedEntries.get(j))
+ push_a_popped = true;
+ else
+ push_all_popped = false;
+ }
+
+ if (!push_a_popped) {
+ // Normal case:
+ // add the instruction and adjust stack depth.
+ newInstructions.addFirst(instr);
+ stackDepth += poppush[0] - poppush[1];
+ continue;
+ }
+
+ /* We push an entry, that gets popped later */
+ int opcode = instr.getOpcode();
+ switch (opcode) {
+ case opc_dup:
+ case opc_dup_x1:
+ case opc_dup_x2:
+ case opc_dup2:
+ case opc_dup2_x1:
+ case opc_dup2_x2: {
+ int popped = 0;
+ for (int j = poppush[1] ; j > 0; j--) {
+ popped <<= 1;
+ if (poppedEntries.get(--stackDepth)) {
+ popped |= 1;
+ poppedEntries.clear(stackDepth);
}
}
- if (succPops == null)
- continue;
- blocks[i].getStackPopPush(poppush);
- int[] myPops = infos[i].poppedPush;
- for (int k = poppush[1]; k < maxStack; k++) {
-
- if (succs.length == 0 || succs[0] == null)
- continue;
- int[] succsPoppedEntries
- = infos[succs[0].getBlockNr()].poppedEntries;
- for (int j = 0; j < succs.length; j++) {
- if (succs[j] == null)
- continue next_block;
- int[] thisPopped
- = infos[succs[j].getBlockNr()].poppedEntries;
-
- for (int k = 0; k < poppedLen; k++) {
- succsPoppedEntries &=
-
-
- for (int j = instrs.length; j-- > 0; ) {
- Instruction instr = instrs[j];
-
- instr.getStackPopPush(poppush);
-
- // Now check if the parameters of this instr are needed.
- boolean params_needed = false;
- switch (instr.getOpcode()) {
- case opc_invokevirtual:
- case opc_invokespecial:
- case opc_invokestatic:
- case opc_invokeinterface:
- case opc_checkcast:
- /* These instructions have side effects, parameters
- * are always needed. Adjust depth.
- */
- params_needed = true;
- depth += poppush[1];
- break;
-
- case opc_pop:
- case opc_pop2:
- break;
-
- default:
- /* Check if all results are needed, adjust depth */
- for (int k = 0; k < poppush[1]; k++) {
- if (!popped.get(depth++))
- params_needed = true;
+ popped = movePopsThroughDup(opcode, newInstructions,
+ popped);
+ for (int j=0; j < poppush[1]; j++) {
+ if ((popped & 1) != 0)
+ poppedEntries.set(stackDepth);
+ stackDepth++;
+ popped >>=1;
+ }
+ break;
+ }
+
+ case opc_swap:
+ if (!push_all_popped) {
+ // swap the popped status
+ if (poppedEntries.get(stackDepth - 1)) {
+ poppedEntries.clear(stackDepth - 1);
+ poppedEntries.set(stackDepth - 2);
+ } else {
+ poppedEntries.set(stackDepth - 1);
+ poppedEntries.clear(stackDepth - 2);
}
- /* If opcode has no result it has side effects. */
- if (poppush[1] == 0)
- params_needed = true;
}
- if (block_pop < maxStack - depth)
- block_pop = maxStack - depth;
-
- if (params_needed) {
- /* mark params as needed */
- for (int k = 0; k < poppush[0]; k++)
- popped.clear(--depth);
+
+ case opc_ldc2_w:
+ case opc_lload: case opc_dload:
+ case opc_i2l: case opc_i2d:
+ case opc_f2l: case opc_f2d:
+ case opc_ldc:
+ case opc_iload: case opc_fload: case opc_aload:
+ case opc_new:
+ case opc_lneg: case opc_dneg:
+ case opc_l2d: case opc_d2l:
+ case opc_laload: case opc_daload:
+ case opc_ineg: case opc_fneg:
+ case opc_i2f: case opc_f2i:
+ case opc_i2b: case opc_i2c: case opc_i2s:
+ case opc_newarray: case opc_anewarray:
+ case opc_arraylength:
+ case opc_instanceof:
+ case opc_lshl: case opc_lshr: case opc_lushr:
+ case opc_iaload: case opc_faload: case opc_aaload:
+ case opc_baload: case opc_caload: case opc_saload:
+ case opc_iadd: case opc_fadd:
+ case opc_isub: case opc_fsub:
+ case opc_imul: case opc_fmul:
+ case opc_idiv: case opc_fdiv:
+ case opc_irem: case opc_frem:
+ case opc_iand: case opc_ior : case opc_ixor:
+ case opc_ishl: case opc_ishr: case opc_iushr:
+ case opc_fcmpl: case opc_fcmpg:
+ case opc_l2i: case opc_l2f:
+ case opc_d2i: case opc_d2f:
+ case opc_ladd: case opc_dadd:
+ case opc_lsub: case opc_dsub:
+ case opc_lmul: case opc_dmul:
+ case opc_ldiv: case opc_ddiv:
+ case opc_lrem: case opc_drem:
+ case opc_land: case opc_lor : case opc_lxor:
+ case opc_lcmp:
+ case opc_dcmpl: case opc_dcmpg:
+ case opc_getstatic:
+ case opc_getfield:
+ case opc_multianewarray:
+
+ /* The simple instructions, that can be removed. */
+ if (!push_all_popped)
+ throw new InternalError("pop half of a long");
+ if (poppush[0] < poppush[1]) {
+ for (int j=0; j < poppush[0] - poppush[1]; j++)
+ poppedEntries.set(stackDepth++);
+ } else if (poppush[0] < poppush[1]) {
+ for (int j=0; j < poppush[0] - poppush[1]; j++)
+ poppedEntries.clear(--stackDepth);
+ }
+
+ case opc_invokevirtual:
+ case opc_invokespecial:
+ case opc_invokestatic:
+ case opc_invokeinterface:
+ case opc_checkcast:
+
+ /* These instructions can't be removed, since
+ * they have side effects.
+ */
+ if (!push_all_popped)
+ throw new InternalError("pop half of a long");
+ if (poppush[1] == 1) {
+ poppedEntries.clear(--stackDepth);
+ newInstructions
+ .addFirst(Instruction.forOpcode(opc_pop));
} else {
- removed[j >> 5] |= 1 << (j & 31);
- /* mark params as popped */
- for (int k = 0; k < poppush[0]; k++)
- popped.set(--depth);
+ poppedEntries.clear(--stackDepth);
+ poppedEntries.clear(--stackDepth);
+ newInstructions
+ .addFirst(Instruction.forOpcode(opc_pop2));
}
+ newInstructions.addFirst(instr);
+ default:
+ throw new InternalError("Unexpected opcode!");
+ }
+ }
+ blocks[i].setCode((Instruction[]) newInstructions
+ .toArray(new Instruction[newInstructions.size()]),
+ blocks[i].getSuccs());
+ return poppedEntries
+ }
+
+ /**
+ * This method analyzes a block from start to end and inserts the
+ * pop instructions at the right places. It is used if a pop couldn't
+ * be removed for some reason.
+ *
+ * @param block the block of code.
+ * @param poppedEntries the stack slots that should be popped at
+ * the end of this block.
+ * @return the stack slots that should be popped at the start of
+ * the block.
+ */
+ void movePopsToTail(Block block, BitSet poppedEntries) {
+ /* Calculate stack height at end of block. */
+ Instruction[] oldInstrs = block.getInstructions();
+ ArrayList newInstructions = new ArrayList();
+
+ int instrNr = oldInstrs.length;
+ int stackDepth = block.getStackHeight();
+ for (instrNr = 0; instrNr < oldInstrs.length; instrNr++) {
+ while (poppedEntries.get(stackDepth-1)) {
+ poppedEntries.clear(--stackDepth);
+ /* XXX opc_pop2?*/
+ newInstructions.add(Instruction.forOpcode(opc_pop));
+ }
+
+ Instruction instr = oldInstrs[--instrNr];
+ instr.getStackPopPush(poppush);
+ for (stackDepth
+
+ /* Check if this instr pushes a popped Entry. */
+ boolean pops_a_popped = false;
+ boolean pops_all_popped = true;
+ for (int j=0; j < poppush[1]; j++) {
+ if (poppedEntries.get(j))
+ push_a_popped = true;
+ else
+ push_all_popped = false;
+ }
+
+ if (!push_a_popped) {
+ // Normal case:
+ // add the instruction and adjust stack depth.
+ newInstructions.addFirst(instr);
+ stackDepth += poppush[0] - poppush[1];
+ continue;
}
- if (blocks[j] == null)
- ;
+ /* We push an entry, that gets popped later */
+ int opcode = instr.getOpcode();
+ switch (opcode) {
+ case opc_dup:
+ case opc_dup_x1:
+ case opc_dup_x2:
+ case opc_dup2:
+ case opc_dup2_x1:
+ case opc_dup2_x2: {
+ int popped = 0;
+ for (int j = poppush[1] ; j > 0; j--) {
+ popped <<= 1;
+ if (poppedEntries.get(--stackDepth)) {
+ popped |= 1;
+ poppedEntries.clear(stackDepth);
+ }
+ }
+ popped = movePopsThroughDup(opcode, newInstructions,
+ popped);
+ for (int j=0; j < poppush[1]; j++) {
+ if ((popped & 1) != 0)
+ poppedEntries.set(stackDepth);
+ stackDepth++;
+ popped >>=1;
}
+ break;
+ }
+
+ case opc_swap:
+ if (!push_all_popped) {
+ // swap the popped status
+ if (poppedEntries.get(stackDepth - 1)) {
+ poppedEntries.clear(stackDepth - 1);
+ poppedEntries.set(stackDepth - 2);
+ } else {
+ poppedEntries.set(stackDepth - 1);
+ poppedEntries.clear(stackDepth - 2);
+ }
+ }
+
+ case opc_ldc2_w:
+ case opc_lload: case opc_dload:
+ case opc_i2l: case opc_i2d:
+ case opc_f2l: case opc_f2d:
+ case opc_ldc:
+ case opc_iload: case opc_fload: case opc_aload:
+ case opc_new:
+ case opc_lneg: case opc_dneg:
+ case opc_l2d: case opc_d2l:
+ case opc_laload: case opc_daload:
+ case opc_ineg: case opc_fneg:
+ case opc_i2f: case opc_f2i:
+ case opc_i2b: case opc_i2c: case opc_i2s:
+ case opc_newarray: case opc_anewarray:
+ case opc_arraylength:
+ case opc_instanceof:
+ case opc_lshl: case opc_lshr: case opc_lushr:
+ case opc_iaload: case opc_faload: case opc_aaload:
+ case opc_baload: case opc_caload: case opc_saload:
+ case opc_iadd: case opc_fadd:
+ case opc_isub: case opc_fsub:
+ case opc_imul: case opc_fmul:
+ case opc_idiv: case opc_fdiv:
+ case opc_irem: case opc_frem:
+ case opc_iand: case opc_ior : case opc_ixor:
+ case opc_ishl: case opc_ishr: case opc_iushr:
+ case opc_fcmpl: case opc_fcmpg:
+ case opc_l2i: case opc_l2f:
+ case opc_d2i: case opc_d2f:
+ case opc_ladd: case opc_dadd:
+ case opc_lsub: case opc_dsub:
+ case opc_lmul: case opc_dmul:
+ case opc_ldiv: case opc_ddiv:
+ case opc_lrem: case opc_drem:
+ case opc_land: case opc_lor : case opc_lxor:
+ case opc_lcmp:
+ case opc_dcmpl: case opc_dcmpg:
+ case opc_getstatic:
+ case opc_getfield:
+ case opc_multianewarray:
+
+ /* The simple instructions, that can be removed. */
+ if (!push_all_popped)
+ throw new InternalError("pop half of a long");
+ if (poppush[0] < poppush[1]) {
+ for (int j=0; j < poppush[0] - poppush[1]; j++)
+ poppedEntries.set(stackDepth++);
+ } else if (poppush[0] < poppush[1]) {
+ for (int j=0; j < poppush[0] - poppush[1]; j++)
+ poppedEntries.clear(--stackDepth);
+ }
+
+ case opc_invokevirtual:
+ case opc_invokespecial:
+ case opc_invokestatic:
+ case opc_invokeinterface:
+ case opc_checkcast:
+
+ /* These instructions can't be removed, since
+ * they have side effects.
+ */
+ if (!push_all_popped)
+ throw new InternalError("pop half of a long");
+ if (poppush[1] == 1) {
+ poppedEntries.clear(--stackDepth);
+ newInstructions
+ .addFirst(Instruction.forOpcode(opc_pop));
+ } else {
+ poppedEntries.clear(--stackDepth);
+ poppedEntries.clear(--stackDepth);
+ newInstructions
+ .addFirst(Instruction.forOpcode(opc_pop2));
+ }
+ newInstructions.addFirst(instr);
+ default:
+ throw new InternalError("Unexpected opcode!");
}
}
- return infos;
+ block.setCode((Instruction[]) newInstructions
+ .toArray(new Instruction[newInstructions.size()]),
+ block.getSuccs());
}
public void transformCode(BasicBlocks bb) {
@@ -262,326 +552,6 @@ public class RemovePopAnalyzer implements CodeTransformer, Opcodes {
boolean poppedEntries[] = new boolean[bb.getMaxStack()];
Block[] blocks = bb.getBlocks();
for (int i = 0; i < blocks.length; i++) {
- LinkedList newInstructions = new LinkedList();
- Instruction[] oldInstrs = blocks[i].getInstructions();
- int instrNr = oldInstrs.length;
- int stackDepth = 0;
- while (instrNr > 0) {
- Instruction instr = oldInstrs[instrNr];
- if (instr.getOpcode() == opc_nop)
- continue;
- if (instr.getOpcode() == opc_pop) {
- poppedEntries[stackDepth++] = true;
- continue;
- }
- if (instr.getOpcode() == opc_pop2) {
- poppedEntries[stackDepth++] = true;
- poppedEntries[stackDepth++] = true;
- continue;
- }
-
- instr.getStackPopPush(poppush);
- // First look if stackDepth was right
- if (stackDepth < poppush[1]) {
- int diff = poppush[1] - stackDepth;
- System.arraycopy(poppedEntries, 0,
- poppedEntries, diff, stackDepth);
- for (int j=0; j< diff; i++)
- poppedEntries[j] = false;
- }
- // Now check if this instr pushes a popped Entry.
- boolean push_a_popped = false;
- boolean push_all_popped = true;
- for (int j=0; j < poppush[1]; j++) {
- if (poppedEntries[j])
- push_a_popped = true;
- else
- push_all_popped = false;
- }
- if (push_a_popped) {
- /* We push an entry, that gets popped later */
- int opcode = instr.getOpcode();
- switch (opcode) {
- case opc_dup:
- case opc_dup_x1:
- case opc_dup_x2:
- case opc_dup2:
- case opc_dup2_x1:
- case opc_dup2_x2: {
-
- int count = (opcode - opc_dup)/3+1;
- int depth = (opcode - opc_dup)%3;
- stackDepth -= count;
- int bottom = stackDepth - count - depth;
-
- int popped1 = 0;
- int popped3 = 0;
- int newDepth = 0;
-
- // get the popped mask and adjust poppedEntries.
- for (int j=0; j< count; j++) {
- if (poppedEntries[bottom + j])
- popped1 |= 1<>= 1;
- popped3 >>= 2;
- } else if ((popped1 & popped3 & 2) != 0) {
- count--;
- popped1 &= 1;
- popped3 &= 1;
- }
-
- if (popped1 == 1) {
- // count == 2, popped1 = 1,
- depth++;
- count--;
- popped1 = 0;
- popped3 >>= 1;
- }
- if (count == 2 && popped1 == 0 && popped3 > 0) {
- // Do the normal dup2 and pop the right
- // element afterwards.
- if (popped3 == 3) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop2));
- } else {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop));
- if (popped3 == 1)
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap));
- }
- popped3 = 0;
- }
- if (popped1 == popped3) {
- // popped1 == popped3 == 0
- // Do a dupcount_xdepth now.
- if (depth < 3) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop - 3 +
- depth + 3 * count));
- break;
- } else {
- // I hope that this will almost never happen.
- // depth = 3, count = 1;
- // Note that instructions are backwards.
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop2)); //DABCD
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x2)); //DABCDAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop)); //DCDAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup_x2)); //DCDABD
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop)); //DCABD
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x2)); //DCABDC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap)); //ABDC
- break;
- }
- }
-
- if (count == 1) {
- // Possible states:
- // popped1 = 0; popped3 = 1;
- // depth = 1 or depth = 2
- if (depth == 1) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap));
- } else {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop));
- newInstructions.addFirst(instr);
- }
- break;
- }
-
- // count = 2; popped1 = 2
- // Possible states:
- // dpth/pop3 0 1
- // 0 AB AAB AB
- // 1 ABC BABC BAC
- // 2 ABCD CABCD CABD
- if (popped3 == 0) {
- if (depth == 0) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap));
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup_x1));
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap));
- } else if (depth == 1) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap)); //BABC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup_x2)); //BACB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap)); //ACB
- } else {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap)); //CABCD
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop2)); //CABDC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x2)); //CABDCAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop)); //CDCAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup_x2)); //CDCABC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop)); //CDABC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x2)); //CDABCD
- }
- } else {
- if (depth == 0) {
- } else if (depth == 1) {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop)); //BAC
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup_x2)); //BACB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_swap)); //ACB
- } else {
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop2)); //CABD
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x1)); //CABDAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_pop2)); //CDAB
- newInstructions.addFirst
- (Instruction.forOpcode(opc_dup2_x2)); //CDABCD
- }
- }
- break;
- }
- case opc_swap: {
- // swap the popped status
- boolean tmp = poppedEntries[stackDepth - 1];
- poppedEntries[stackDepth - 1]
- = poppedEntries[stackDepth - 2];
- poppedEntries[stackDepth - 2] = tmp;
- }
-
-
- // Now the simple instructions, that can be removed.
- // delta = -2;
- case opc_ldc2_w:
- case opc_lload: case opc_dload:
- if (!push_all_popped)
- throw new InternalError("pop half of a long");
- poppedEntries[--stackDepth] = false;
- poppedEntries[--stackDepth] = false;
- continue;
-
- case opc_i2l: case opc_i2d:
- case opc_f2l: case opc_f2d:
- case opc_ldc:
- case opc_iload: case opc_fload: case opc_aload:
- case opc_new:
- case opc_lneg: case opc_dneg:
- case opc_l2d: case opc_d2l:
- case opc_laload: case opc_daload:
- case opc_ineg: case opc_fneg:
- case opc_i2f: case opc_f2i:
- case opc_i2b: case opc_i2c: case opc_i2s:
- case opc_newarray: case opc_anewarray:
- case opc_arraylength:
- case opc_instanceof:
- case opc_lshl: case opc_lshr: case opc_lushr:
- case opc_iaload: case opc_faload: case opc_aaload:
- case opc_baload: case opc_caload: case opc_saload:
- case opc_iadd: case opc_fadd:
- case opc_isub: case opc_fsub:
- case opc_imul: case opc_fmul:
- case opc_idiv: case opc_fdiv:
- case opc_irem: case opc_frem:
- case opc_iand: case opc_ior : case opc_ixor:
- case opc_ishl: case opc_ishr: case opc_iushr:
- case opc_fcmpl: case opc_fcmpg:
- case opc_l2i: case opc_l2f:
- case opc_d2i: case opc_d2f:
- case opc_ladd: case opc_dadd:
- case opc_lsub: case opc_dsub:
- case opc_lmul: case opc_dmul:
- case opc_ldiv: case opc_ddiv:
- case opc_lrem: case opc_drem:
- case opc_land: case opc_lor : case opc_lxor:
- case opc_lcmp:
- case opc_dcmpl: case opc_dcmpg:
- case opc_getstatic:
- case opc_getfield:
- case opc_multianewarray:
-
- if (!push_all_popped)
- throw new InternalError("pop half of a long");
- if (poppush[0] < poppush[1]) {
- for (int j=0; j < poppush[0] - poppush[1]; j++)
- poppedEntries[stackDepth++] = true;
- } else if (poppush[0] < poppush[1]) {
- for (int j=0; j < poppush[0] - poppush[1]; j++)
- poppedEntries[--stackDepth] = false;
- }
-
- case opc_invokevirtual:
- case opc_invokespecial:
- case opc_invokestatic:
- case opc_invokeinterface:
- case opc_checkcast:
- if (!push_all_popped)
- throw new InternalError("pop half of a long");
- if (poppush[1] == 1) {
- poppedEntries[--stackDepth] = false;
- newInstructions
- .addFirst(Instruction.forOpcode(opc_pop));
- } else {
- poppedEntries[--stackDepth] = false;
- poppedEntries[--stackDepth] = false;
- newInstructions
- .addFirst(Instruction.forOpcode(opc_pop2));
- }
- newInstructions.addFirst(instr);
- default:
- throw new InternalError("Unexpected opcode!");
- }
- } else {
- // Add the instruction ..
- newInstructions.addFirst(instr);
- // .. and adjust stackDepth.
- stackDepth += poppush[0] - poppush[1];
- }
- }
- for (int j=0; j < stackDepth; j++) {
- // XXXX
- }
blocks[i].setCode((Instruction[]) newInstructions
.toArray(oldInstrs), blocks[i].getSuccs());
}
diff --git a/jode/src/net/sf/jode/util/SimpleMap.java b/jode/src/net/sf/jode/util/SimpleMap.java
index 4ca2eef..b900c32 100644
--- a/jode/src/net/sf/jode/util/SimpleMap.java
+++ b/jode/src/net/sf/jode/util/SimpleMap.java
@@ -96,5 +96,3 @@ public class SimpleMap extends AbstractMap {
return null;
}
}
-
-