From 4352b285abb63305ec86d96a8e0c3c81b23b39ad Mon Sep 17 00:00:00 2001
From: hoenicke
Date: Mon, 6 Aug 2001 13:39:04 +0000
Subject: [PATCH] Documentation updates (INSTALL, javadoc). Added JUnit Test
cases. * build.xml: Big update. * net/sf/jode/bytecode/BasicBlock.java:
(updateMaxStackLocals): new method to calculate maxStack and maxLocals.
(setBlocks): fixed calculation of handlers, call updateMaxLocals. *
net/sf/jode/bytecode/BasicBlockReader.java: (maxLocals, maxStack): new
fields. (readCode): read maxStack/Locals into private fields. (convert):
check that maxStack/Locals match what we calculate. *
net/sf/jode/bytecode/BinaryInfo.java: (getKnownAttributeCount): renamed to...
(getAttributeCount): ... this, and also count internal attributes. Made it
protected. (readAttribute): made protected. (drop): made protected.
(prepareAttributes): made protected. (writeKnownAttributes): removed.
(writeAttributes): made protected, use getAttributeCount. Changed policy: it
doesn't call writeKnownAttribute, but instead it expects sub classes to
override this method. (getAttributeSize): made protected, subclasses should
override it. Changed all subclasses to new policy. *
net/sf/jode/bytecode/Block.java: (lineNr): Removed, it wasn't used.
(pop,push): Removed, replaced by ... (maxpop,maxpush,delta): ... these, with
slightly changed semantics. (stackHeight): New variable. (Block): Default
Constructor doesn't initialize fields now. (getCatchers): Renamed to ...
(getHandlers): ... this, changed all callers. (initCode): Calculate maxpop,
maxpush, delta correctly. (getStackPopPush): Changed accordingly to new
fields. (setCode): Removed debugging output for illegal contents. *
net/sf/jode/bytecode/Classes.java: Reworked handling of inner classes.
(innerClasses): Field mustn't be null anymore when loaded. (setName): Update
class in classpath. * net/sf/jode/bytecode/ClassPath.java: (renameClassInfo):
new function, should only used by ClassInfo. *
net/sf/jode/bytecode/ConstantPool.java: made public.
(getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
(iterateClassNames): New method. * net/sf/jode/decompiler/Main.java:
(decompileClass): Catch ClassFormatExceptions and decompile remaining
classes. * net/sf/jode/obfuscator/ClassIdentifier.java: Updated handling of
inner/extra classes to new ClassInfo behaviour. (initSuperClasses): Load
DECLARATION of super classes. *
net/sf/jode/obfuscator/PackageIdentifier.java: Replace deprecated methods of
ClassInfo with corresponding classpath calls. (loadMatchingClasses):
Initialize packages loaded on demand if we are initialize. *
net/sf/jode/obfuscator/modules/ConstantAnalyzer.java: Now extends
SimpleAnalyzer. (canonizeIfaceRef): Removed; it is now inherited.
(canonizeRef): likewise. Big updates to handle jsr correctly. (handleOpcode):
Moved method to BlockInfo. *
net/sf/jode/obfuscator/modules/SimpleAnalyzer.java: (canonizeIfaceRef): New
method, copied from ConstantAnalyzer. (canonizeRef): call canonizeIfaceRef
for interfaces. * net/sf/jode/util/UnifyHash.java (iterateHashCode): iterator
now supports remove(). (remove): New method.
git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1337 379699f6-c40d-0410-875b-85095c16579e
---
jode/ChangeLog | 98 +-
jode/INSTALL | 62 +-
jode/build.xml | 306 ++-
jode/scripts/jcpp.pl | 6 +-
.../sf/jode/bytecode/BasicBlockReader.java | 16 +-
.../sf/jode/bytecode/BasicBlockWriter.java | 208 +-
.../src/net/sf/jode/bytecode/BasicBlocks.java | 108 +-
jode/src/net/sf/jode/bytecode/BinaryInfo.java | 133 +-
jode/src/net/sf/jode/bytecode/Block.java | 65 +-
jode/src/net/sf/jode/bytecode/ClassInfo.java | 406 ++--
jode/src/net/sf/jode/bytecode/ClassPath.java | 22 +-
.../net/sf/jode/bytecode/ConstantPool.java | 62 +-
jode/src/net/sf/jode/bytecode/FieldInfo.java | 19 +-
jode/src/net/sf/jode/bytecode/Handler.java | 4 +-
jode/src/net/sf/jode/bytecode/MethodInfo.java | 58 +-
jode/src/net/sf/jode/bytecode/Subroutine.java | 73 -
.../sf/jode/bytecode/TransformSubroutine.java | 228 ---
jode/src/net/sf/jode/bytecode/package.html | 16 +-
.../net/sf/jode/decompiler/Decompiler.java | 3 -
jode/src/net/sf/jode/decompiler/Main.java | 5 +
.../sf/jode/decompiler/ProgressListener.java | 2 +-
jode/src/net/sf/jode/flow/FlowBlock.java | 2 +-
jode/src/net/sf/jode/flow/Jump.java | 2 +-
jode/src/net/sf/jode/flow/VariableStack.java | 4 +-
jode/src/net/sf/jode/jvm/CodeVerifier.java | 16 +-
jode/src/net/sf/jode/jvm/Interpreter.java | 2 +-
.../sf/jode/obfuscator/ClassIdentifier.java | 64 +-
.../sf/jode/obfuscator/PackageIdentifier.java | 22 +-
.../obfuscator/modules/ConstantAnalyzer.java | 1753 ++++++++---------
.../obfuscator/modules/SimpleAnalyzer.java | 34 +-
jode/src/net/sf/jode/util/UnifyHash.java | 31 +-
31 files changed, 1967 insertions(+), 1863 deletions(-)
delete mode 100644 jode/src/net/sf/jode/bytecode/Subroutine.java
delete mode 100644 jode/src/net/sf/jode/bytecode/TransformSubroutine.java
diff --git a/jode/ChangeLog b/jode/ChangeLog
index 6f538ab..3b32dbd 100644
--- a/jode/ChangeLog
+++ b/jode/ChangeLog
@@ -1,3 +1,73 @@
+2001-08-05 Jochen Hoenicke
+
+ Documentation updates (INSTALL, javadoc).
+ Added JUnit Test cases.
+ * build.xml: Big update.
+ * net/sf/jode/bytecode/BasicBlock.java:
+ (updateMaxStackLocals): new method to calculate maxStack and
+ maxLocals.
+ (setBlocks): fixed calculation of handlers, call updateMaxLocals.
+ * net/sf/jode/bytecode/BasicBlockReader.java:
+ (maxLocals, maxStack): new fields.
+ (readCode): read maxStack/Locals into private fields.
+ (convert): check that maxStack/Locals match what we calculate.
+ * net/sf/jode/bytecode/BinaryInfo.java:
+ (getKnownAttributeCount): renamed to...
+ (getAttributeCount): ... this, and also count internal attributes.
+ Made it protected.
+ (readAttribute): made protected.
+ (drop): made protected.
+ (prepareAttributes): made protected.
+ (writeKnownAttributes): removed.
+ (writeAttributes): made protected, use getAttributeCount.
+ Changed policy: it doesn't call writeKnownAttribute, but instead
+ it expects sub classes to override this method.
+ (getAttributeSize): made protected, subclasses should override it.
+ Changed all subclasses to new policy.
+ * net/sf/jode/bytecode/Block.java:
+ (lineNr): Removed, it wasn't used.
+ (pop,push): Removed, replaced by ...
+ (maxpop,maxpush,delta): ... these, with slightly changed semantics.
+ (stackHeight): New variable.
+ (Block): Default Constructor doesn't initialize fields now.
+ (getCatchers): Renamed to ...
+ (getHandlers): ... this, changed all callers.
+ (initCode): Calculate maxpop, maxpush, delta correctly.
+ (getStackPopPush): Changed accordingly to new fields.
+ (setCode): Removed debugging output for illegal contents.
+ * net/sf/jode/bytecode/Classes.java: Reworked handling of inner
+ classes.
+ (innerClasses): Field mustn't be null anymore when loaded.
+ (setName): Update class in classpath.
+ * net/sf/jode/bytecode/ClassPath.java:
+ (renameClassInfo): new function, should only used by ClassInfo.
+ * net/sf/jode/bytecode/ConstantPool.java: made public.
+ (getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
+ (iterateClassNames): New method.
+ * net/sf/jode/decompiler/Main.java:
+ (decompileClass): Catch ClassFormatExceptions and decompile
+ remaining classes.
+ * net/sf/jode/obfuscator/ClassIdentifier.java:
+ Updated handling of inner/extra classes to new ClassInfo behaviour.
+ (initSuperClasses): Load DECLARATION of super classes.
+ * net/sf/jode/obfuscator/PackageIdentifier.java:
+ Replace deprecated methods of ClassInfo with corresponding classpath
+ calls.
+ (loadMatchingClasses): Initialize packages loaded on demand if we
+ are initialize.
+ * net/sf/jode/obfuscator/modules/ConstantAnalyzer.java:
+ Now extends SimpleAnalyzer.
+ (canonizeIfaceRef): Removed; it is now inherited.
+ (canonizeRef): likewise.
+ Big updates to handle jsr correctly.
+ (handleOpcode): Moved method to BlockInfo.
+ * net/sf/jode/obfuscator/modules/SimpleAnalyzer.java:
+ (canonizeIfaceRef): New method, copied from ConstantAnalyzer.
+ (canonizeRef): call canonizeIfaceRef for interfaces.
+ * net/sf/jode/util/UnifyHash.java
+ (iterateHashCode): iterator now supports remove().
+ (remove): New method.
+
2001-07-30 Jochen Hoenicke
Changed compilation procedure to ant.
@@ -149,8 +219,8 @@
2001-07-15 Jochen Hoenicke
Applied patch from 2001-05-08 of Jode 1.1 tree:
* jode/jvm/CodeVerifier.java (doVerify): Don't check for
- uninitialized objects in local or stack slots on backwards jump or
- exception blocks. Sun's jdk also doesn't check it, and I never
+ uninitialized objects in local or stack slots on backwards jump or
+ exception blocks. Sun's jdk also doesn't check it, and I never
understood why it is necessary. But see JVM Spec 4.9.4.
2001-07-15 Jochen Hoenicke
@@ -238,12 +308,12 @@
* jode/decompiler/Options.java (setOptions): changed gnu style
to include GNU_SPACING.
* jode/decompiler/ClassAnalyzer.java (dumpSource): Use
- open/closeBraceClass.
+ open/closeBraceClass.
* jode/decompiler/MethodAnalyzer.java (dumpSource): Use
- open/closeBraceNoIndent. Call printOptionalSpace.
+ open/closeBraceNoIndent. Call printOptionalSpace.
* jode/decompiler/InvokeOperator.java (dumpExpression):
Call printOptionalSpace, use open/closeBraceClass for inner
- classes.
+ classes.
* jode/decompiler/UnaryOperator.java (dumpExpression): Call
printOptionalSpace.
@@ -252,21 +322,21 @@
* jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT):
new constant.
(openBrace, openBraceContinue, closeBrace, closeBraceNoSpace,
- closeBraceContinue): handle flush left.
+ closeBraceContinue): handle flush left.
* jode/type/NullType.java (intersection): Removed, since the
- version in ReferenceType is more correct. Before
- tNull.isOfType(tRange(X,tNull)) returned false, which lead to
- incorrect behaviour in InvokeOperator.needsCast.
+ version in ReferenceType is more correct. Before
+ tNull.isOfType(tRange(X,tNull)) returned false, which lead to
+ incorrect behaviour in InvokeOperator.needsCast.
* jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the
- "= null" hack for final fields; it was not correct, since the
- field could be initialized in a constructor.
+ "= null" hack for final fields; it was not correct, since the
+ field could be initialized in a constructor.
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp):
Simplified the code, copy options always from child.
* jode/expr/InvokeOperator.java (isGetClass): Allow the method to
- be declared inside an outer class: We simply check if we can get
+ be declared inside an outer class: We simply check if we can get
the method analyzer.
(simplify): handle unifyParam.
* jode/expr/PopOperator.java (getBreakPenalty): return penalty of
- inner expression. (dumpExpression): Call dumpExpression of
- subexpression immediately without priority.
+ inner expression. (dumpExpression): Call dumpExpression of
+ subexpression immediately without priority.
diff --git a/jode/INSTALL b/jode/INSTALL
index 0fdb310..dfce9a9 100644
--- a/jode/INSTALL
+++ b/jode/INSTALL
@@ -1,45 +1,23 @@
Before installing, make sure you have at least version 1.1 of the java
developement kit installed. If you want to run this program you only
need the java runtime environment. Version 1.1 is quite old, I
-recommend using Java 2 (jdk1.2 or above).
-
-This package was designed to use the GNU standard for configuration
-and makefiles. To build and install do the following:
-
-0). If you have downloaded the code from the CVS repository create
-configure and Makefile.in with autoconf-2.13 and automake-1.4. Type
-"aclocal; automake -a; autoconf".
-
-1). You need a java development kit (at least version 1.1), some unix
-tools and some java packages. Make sure that you have all java
-packages that are needed in your classpath. This are gnu.getopt, and
-if you have JDK 1.1 you also need the collection classes and swing for
-1.1. These packages are accessible from the following urls:
-
- CYGWIN (unix tools for win95/NT):
- http://sourceware.cygnus.com/cygwin/
-
- JDK 1.1:
- http://java.sun.com/products/jdk/1.1/index.htm
-
- Collection classes and Swing for JDK 1.1:
- http://java.sun.com/beans/infobus/#DOWNLOAD_COLLECTIONS
- http://java.sun.com/products/jfc/index.html#download-swing
-
- JDK 1.2:
- http://java.sun.com/products/jdk/1.2/index.html
-
- Getopt:
- http://www.urbanophile.com/arenn/hacking/download.html#getopt
-
-2). Run the "configure" script to configure the package. There are
-various options you might want to pass to configure to control how the
-package is built. "configure --help" will give a complete list.
-
-Use the --with-java option to specify the install directory of the jdk.
-
-If you have jikes, you should specify it with --with-jikes. You can
-give a path to the directory where it resides, otherwise it is
-searched in the path.
-
-3). Type "make" to build the package.
+recommend using Java 2 (jdk1.2 or above). You need perl if you want
+to compile a 1.1 version.
+
+This package was designed to use the ANT from the jakarta.apache.org
+tools. I assume you have installed it correctly.
+
+Take some time to edit config.props. There are a few options you need
+to take care of. (Unfortunately ant can't test for executables).
+
+Now you are ready to invoke ant. There are many possible targets, here
+are the most useful ones:
+
+all builds class files and documentation.
+build builds class files only (autodetects java version).
+build-1.1 builds JDK1.1 class files.
+doc builds documentation.
+dist creates all release files.
+test does some self tests. You need to have junit installed for this.
+clean cleans everything that doesn't belong to the source distribution.
+cvsclean cleans everything that doesn't belong into the cvs repository.
diff --git a/jode/build.xml b/jode/build.xml
index 9f545bb..f15fa07 100644
--- a/jode/build.xml
+++ b/jode/build.xml
@@ -19,52 +19,75 @@
-->
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -93,91 +116,191 @@
value="com.sun.java.util.collections"
classname="com.sun.java.util.collections.Set"
classpathref="project.classpath" />
-
+
-
-
+
+
+
+
-
-
+
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+ destdir="${api.doc}"
+ use="yes">
+
+
-
+
+
+
-
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
@@ -185,13 +308,30 @@
+
-
+
+
+
+
diff --git a/jode/scripts/jcpp.pl b/jode/scripts/jcpp.pl
index cef96f4..7f886f8 100755
--- a/jode/scripts/jcpp.pl
+++ b/jode/scripts/jcpp.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
-# jcpp Copyright (C) 1999 Jochen Hoenicke.
+# jcpp Copyright (C) 1999-2001 Jochen Hoenicke.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -39,8 +39,8 @@
# After running jcpp the false branch is commented out. If the true
# branch was commented out it will get commented in.
#
-# jcpp also definitions, useful for package renaming. The java file
-# must look like this:
+# jcpp can also change definitions, useful for package renaming. The
+# java file should look like this:
#
# ///#def COLLECTIONS java.util
# import java.util.Vector
diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java
index 8c6db86..7b65077 100644
--- a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java
+++ b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java
@@ -75,6 +75,9 @@ class BasicBlockReader implements Opcodes {
BasicBlocks bb;
Block[] blocks;
+ int maxStack;
+ int maxLocals;
+
public BasicBlockReader(BasicBlocks bb) {
this.bb = bb;
}
@@ -298,13 +301,20 @@ class BasicBlockReader implements Opcodes {
if (start != -1)
convertBlock(start, count);
bb.setBlocks(blocks, getSuccBlock(0), convertHandlers());
+ if (bb.maxStack > maxStack)
+ throw new ClassFormatException("Only allocated "+maxStack
+ +" stack slots for method, needs "
+ +bb.maxStack);
+ if (bb.maxLocals > maxLocals)
+ throw new ClassFormatException("Only allocated "+maxLocals
+ +" local slots for method, needs "
+ +bb.maxLocals);
}
public void readCode(ConstantPool cp,
DataInputStream input) throws IOException {
- bb.maxStack = input.readUnsignedShort();
- int maxLocals = input.readUnsignedShort();
- bb.maxLocals = maxLocals;
+ maxStack = input.readUnsignedShort();
+ maxLocals = input.readUnsignedShort();
int codeLength = input.readInt();
infos = new InstrInfo[codeLength];
diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
index 0a7ec37..b820929 100644
--- a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
+++ b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
@@ -76,8 +76,10 @@ class BasicBlockWriter implements Opcodes {
new LocalVariableInfo[blocks.length][];
int startBlockNr = startBlock.getBlockNr();
atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()];
- for (int i=0; i < bb.getParamCount(); i++)
- atStart[startBlockNr][i] = bb.getParamInfo(i);
+ for (int i=0; i < bb.getParamCount(); i++) {
+ LocalVariableInfo lvi = bb.getParamInfo(i);
+ atStart[startBlockNr][i] = lvi.getName() != null ? lvi : null;
+ }
/* We currently ignore the jsr/ret issue. Should be okay,
* though, since it can only generate a bit too much local
@@ -94,36 +96,47 @@ class BasicBlockWriter implements Opcodes {
if (instrs[i].hasLocal()) {
LocalVariableInfo lvi = instrs[i].getLocalInfo();
int slot = lvi.getSlot();
- if (life[slot] != null
- && life[slot] != lvi)
- life[slot] = null;
-
- if (life[slot] == null
- && lvi.getName() != null)
- life[slot] = lvi;
+ life[slot] = lvi.getName() != null ? lvi : null;
}
}
Block[] succs = block.getSuccs();
- if (succs != null) {
- for (int j = 0; j < succs.length; j++) {
- if (succs[j] == null)
- continue;
- int succNr = succs[j].getBlockNr();
- if (atStart[succNr] == null) {
- atStart[succNr] = (LocalVariableInfo[]) life.clone();
+ for (int j = 0; j < succs.length; j++) {
+ if (succs[j] == null)
+ continue;
+ int succNr = succs[j].getBlockNr();
+ if (atStart[succNr] == null) {
+ atStart[succNr] = (LocalVariableInfo[]) life.clone();
+ todo.push(succs[j]);
+ } else {
+ boolean changed = false;
+ for (int k = 0; k < life.length; k++) {
+ if (atStart[succNr][k] != life[k]
+ && atStart[succNr][k] != null) {
+ atStart[succNr][k] = null;
+ changed = true;
+ }
+ }
+ if (changed && !todo.contains(succs[j]))
todo.push(succs[j]);
- } else {
- boolean changed = false;
- for (int k = 0; k < life.length; k++) {
- if (atStart[succNr][k] != life[k]
- && atStart[succNr][k] != null) {
- atStart[succNr][k] = null;
- changed = true;
- }
+ }
+ }
+ Handler[] handlers = block.getHandlers();
+ for (int j = 0; j < handlers.length; j++) {
+ int succNr = handlers[j].getCatcher().getBlockNr();
+ if (atStart[succNr] == null) {
+ atStart[succNr] = (LocalVariableInfo[]) life.clone();
+ todo.push(handlers[j].getCatcher());
+ } else {
+ boolean changed = false;
+ for (int k = 0; k < life.length; k++) {
+ if (atStart[succNr][k] != life[k]
+ && atStart[succNr][k] != null) {
+ atStart[succNr][k] = null;
+ changed = true;
}
- if (changed && !todo.contains(succs[j]))
- todo.push(succs[j]);
}
+ if (changed && !todo.contains(handlers[j].getCatcher()))
+ todo.push(handlers[j].getCatcher());
}
}
}
@@ -137,6 +150,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry();
current[slot].startAddr = 0;
current[slot].lvi = lvi;
+ System.err.println("lvi at init,"+slot+": "+lvi);
}
}
@@ -158,6 +172,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry();
current[slot].startAddr = addr;
current[slot].lvi = atStart[i][slot];
+ System.err.println("lvi at "+i+","+slot+": "+current[slot].lvi);
}
}
@@ -178,6 +193,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry();
current[slot].startAddr = addr;
current[slot].lvi = lvi;
+ System.err.println("lvi at "+i+","+k+","+slot+": "+current[slot].lvi);
}
}
addr += instrLength[i][k];
@@ -222,8 +238,8 @@ class BasicBlockWriter implements Opcodes {
gotos[0] = startBlock.getBlockNr();
}
- next_block:
for (int i = 0; i < blocks.length; i++) {
+ boolean hasDefaultSucc = true;
blockAddr[i] = addr;
Instruction[] instrs = blocks[i].getInstructions();
instrLength[i] = new int[instrs.length];
@@ -265,7 +281,7 @@ class BasicBlockWriter implements Opcodes {
} else if (value >= Short.MIN_VALUE
&& value <= Short.MAX_VALUE) {
length = 3;
- break switch_opc;
+ break switch_opc;
}
}
if (gcp.putConstant(constant) < 256) {
@@ -304,30 +320,28 @@ class BasicBlockWriter implements Opcodes {
length = 2;
else
length = 4;
- gotos[i+1] = -2;
+ hasDefaultSucc = false;
break;
}
case opc_lookupswitch: {
- length = 3-(addr % 4);
+ length = (~addr) & 3; /* padding */
int[] values = instr.getValues();
int npairs = values.length;
for (int k=0; k< succs.length; k++) {
if (succs[k] == null)
needRet = true;
}
- if (npairs > 0) {
- int tablesize = values[npairs-1] - values[0] + 1;
- if (4 + tablesize * 4 < 8 * npairs) {
- // Use a table switch
- length += 13 + 4 * tablesize;
- break;
- }
+ if (npairs > 0
+ && 4 + 4 * (values[npairs-1] - values[0] + 1)
+ <= 8 * npairs) {
+ // Use a table switch
+ length += 13 + 4 * (values[npairs-1] - values[0] + 1);
+ } else {
+ // Use a lookup switch
+ length += 9 + 8 * npairs;
}
- // Use a lookup switch
- length += 9 + 8 * npairs;
- // The goto is inclusive through the default part.
- gotos[i+1] = -2;
- continue next_block;
+ hasDefaultSucc = false;
+ break;
}
case opc_jsr:
conds[i+1] = succs[0].getBlockNr();
@@ -391,8 +405,8 @@ class BasicBlockWriter implements Opcodes {
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_return:
case opc_athrow:
- gotos[i+1] = -2;
length = 1;
+ hasDefaultSucc = false;
break;
case opc_nop:
case opc_iaload: case opc_laload: case opc_faload:
@@ -434,20 +448,26 @@ class BasicBlockWriter implements Opcodes {
instrLength[i][j] = length;
addr += length;
}
- Block defaultSucc = succs[succs.length-1];
- if (defaultSucc == null) {
- // This is a return
- gotos[i+1] = -1;
- isRet.set(i+1);
- lastRetAddr = addr;
- addr++;
- } else if (defaultSucc.getBlockNr() == i + 1) {
- // no need for any jump
- gotos[i+1] = succs[succs.length-1].getBlockNr();
+ if (hasDefaultSucc) {
+ Block defaultSucc = succs[succs.length-1];
+ if (defaultSucc == null) {
+ // This is a return
+ gotos[i+1] = -1;
+ isRet.set(i+1);
+ lastRetAddr = addr;
+ hasRet = true;
+ addr++;
+ } else if (defaultSucc.getBlockNr() == i + 1) {
+ // no need for any jump
+ gotos[i+1] = succs[succs.length-1].getBlockNr();
+ } else {
+ // Reserve space for a normal goto.
+ gotos[i+1] = succs[succs.length-1].getBlockNr();
+ addr += 3;
+ }
} else {
- // Reserve space for a normal goto.
- gotos[i+1] = succs[succs.length-1].getBlockNr();
- addr += 3;
+ // No goto needed for this block
+ gotos[i+1] = -2;
}
}
if (needRet && !hasRet) {
@@ -470,9 +490,9 @@ class BasicBlockWriter implements Opcodes {
int from = blockAddr[i] - 3;
if (gotoNr != i + 1)
from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3;
- int dist;
+ int dist = Integer.MAX_VALUE;
if (condNr == -1) {
- if (!retAtEnd) {
+ if (retAtEnd) {
dist = blockAddr[blockAddr.length-1] - 1 - from;
} else {
for (int j = 0; j < gotos.length; j++) {
@@ -483,7 +503,8 @@ class BasicBlockWriter implements Opcodes {
break;
}
}
- throw new InternalError();
+ if (dist == Integer.MAX_VALUE)
+ throw new InternalError();
}
} else {
dist = blockAddr[condNr] - from;
@@ -502,7 +523,7 @@ class BasicBlockWriter implements Opcodes {
blockAddr[j] += diff;
changed = true;
}
- }
+ }
if (!isWide.get(i) && gotoNr >= 0) {
int dist = blockAddr[gotoNr] - blockAddr[i] + 3;
if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) {
@@ -565,6 +586,7 @@ class BasicBlockWriter implements Opcodes {
gcp.putUTF8("LocalVariableTable");
int count = lvt.length;
for (int i=0; i < count; i++) {
+ System.err.println("lvt: "+lvt[i].lvi);
gcp.putUTF8(lvt[i].lvi.getName());
gcp.putUTF8(lvt[i].lvi.getType());
}
@@ -590,13 +612,12 @@ class BasicBlockWriter implements Opcodes {
output.writeShort(lvt[i].lvi.getSlot());
}
}
- if (lnt != null) {
+ if (lntCount > 0) {
output.writeShort(gcp.putUTF8("LineNumberTable"));
- int count = lnt.length / 2;
- int length = 2 + 4 * count;
+ int length = 2 + 4 * lntCount;
output.writeInt(length);
- output.writeShort(count);
- for (int i=0; i < count; i++) {
+ output.writeShort(lntCount);
+ for (int i = 0; i < lntCount; i++) {
output.writeShort(lnt[2*i]);
output.writeShort(lnt[2*i+1]);
}
@@ -629,11 +650,11 @@ class BasicBlockWriter implements Opcodes {
}
int lntPtr = 0;
- next_block:
for (int i = 0; i< blocks.length; i++) {
+ boolean hasDefaultSucc = true;
Block[] succs = blocks[i].getSuccs();
if (addr != blockAddr[i])
- throw new InternalError("Address calculation broken!");
+ throw new InternalError("Address calculation broken for "+i+": "+blockAddr[i]+"!="+addr+"!");
Instruction[] instructions = blocks[i].getInstructions();
int size = instructions.length;
for (int j = 0; j < size; j++) {
@@ -679,7 +700,8 @@ class BasicBlockWriter implements Opcodes {
output.writeByte(opcode);
output.writeShort(slot);
}
- continue next_block;
+ hasDefaultSucc = false;
+ break;
}
case opc_ldc:
case opc_ldc2_w: {
@@ -782,18 +804,19 @@ class BasicBlockWriter implements Opcodes {
} else {
int dist;
if (dest == null) {
- if (!retAtEnd) {
+ if (retAtEnd) {
dist = blockAddr[blocks.length] - 1 - addr;
} else {
- for (int k = 0; k < blocks.length + 1; k++) {
+ for (int k = 0; ; k++) {
if (isRet.get(k)) {
dist = blockAddr[k] - 1 - addr;
if (dist >= Short.MIN_VALUE
&& dist <= Short.MAX_VALUE)
break;
}
+ if (k == blocks.length)
+ throw new InternalError();
}
- throw new InternalError();
}
} else {
dist = blockAddr[dest.getBlockNr()] - addr;
@@ -814,7 +837,7 @@ class BasicBlockWriter implements Opcodes {
if (npairs > 0) {
int tablesize = values[npairs-1] - values[0] + 1;
- if (4 + tablesize * 4 < 8 * npairs) {
+ if (4 + tablesize * 4 <= 8 * npairs) {
// Use a table switch
output.writeByte(opc_tableswitch);
output.write(new byte[align]);
@@ -832,7 +855,8 @@ class BasicBlockWriter implements Opcodes {
: blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr);
}
- continue next_block;
+ hasDefaultSucc = false;
+ break;
}
}
// Use a lookup switch
@@ -847,7 +871,8 @@ class BasicBlockWriter implements Opcodes {
: blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr);
}
- continue next_block;
+ hasDefaultSucc = false;
+ break;
}
case opc_getstatic:
@@ -903,7 +928,9 @@ class BasicBlockWriter implements Opcodes {
case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_athrow: case opc_return:
- continue next_block;
+ output.writeByte(opcode);
+ hasDefaultSucc = false;
+ break;
case opc_nop:
case opc_iaload: case opc_laload: case opc_faload:
@@ -944,19 +971,23 @@ class BasicBlockWriter implements Opcodes {
}
addr += instrLength[i][j];
}
- // Check which type of goto we should use at end of this block.
- Block defaultSucc = succs[succs.length - 1];
- if (isRet.get(i+1)) {
- output.writeByte(opc_return);
- addr++;
- } else if (isWide.get(i+1)) {
- output.writeByte(opc_goto_w);
- output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr);
- addr+=5;
- } else if (defaultSucc.getBlockNr() != i+1) {
- output.writeByte(opc_goto);
- output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr);
- addr+=3;
+ if (hasDefaultSucc) {
+ // Check which type of goto we should use at end of this block.
+ Block defaultSucc = succs[succs.length - 1];
+ if (isRet.get(i+1)) {
+ output.writeByte(opc_return);
+ addr++;
+ } else if (isWide.get(i+1)) {
+ output.writeByte(opc_goto_w);
+ output.writeInt(blockAddr[defaultSucc.getBlockNr()]
+ - addr);
+ addr+=5;
+ } else if (defaultSucc.getBlockNr() != i+1) {
+ output.writeByte(opc_goto);
+ output.writeShort(blockAddr[defaultSucc.getBlockNr()]
+ - addr);
+ addr+=3;
+ }
}
}
if (retAtEnd) {
@@ -977,4 +1008,3 @@ class BasicBlockWriter implements Opcodes {
}
}
}
-
diff --git a/jode/src/net/sf/jode/bytecode/BasicBlocks.java b/jode/src/net/sf/jode/bytecode/BasicBlocks.java
index dd0812b..8975096 100644
--- a/jode/src/net/sf/jode/bytecode/BasicBlocks.java
+++ b/jode/src/net/sf/jode/bytecode/BasicBlocks.java
@@ -26,6 +26,8 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.BitSet;
+import java.util.Stack;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Arrays;
@@ -68,8 +70,9 @@ import java.lang.UnsupportedOperationException;
* necessary, you don't have to care about that.
*
* @see net.sf.jode.bytecode.Block
- * @see net.sf.jode.bytecode.Instruction */
-public class BasicBlocks extends BinaryInfo {
+ * @see net.sf.jode.bytecode.Instruction
+ */
+public class BasicBlocks extends BinaryInfo implements Opcodes {
/**
* The method info which contains the basic blocks.
@@ -153,12 +156,84 @@ public class BasicBlocks extends BinaryInfo {
return paramInfos.length;
}
- public void setMaxStack(int ms) {
- maxStack = ms;
- }
+ /**
+ * Updates the maxStack and maxLocals according to the current code.
+ * Call this every time you change the code.
+ */
+ public void updateMaxStackLocals() {
+ maxLocals = getParamCount();
+ maxStack = 0;
- public void setMaxLocals(int ml) {
- maxLocals = ml;
+ if (startBlock == null)
+ return;
+
+ BitSet visited = new BitSet();
+ Stack todo = new Stack();
+ int[] poppush = new int[2];
+
+ startBlock.stackHeight = 0;
+ todo.push(startBlock);
+ while (!todo.isEmpty()) {
+ Block block = (Block) todo.pop();
+ int stackHeight = block.stackHeight;
+ if (stackHeight + block.maxpush > maxStack)
+ maxStack = stackHeight + block.maxpush;
+ stackHeight += block.delta;
+
+ Block[] succs = block.getSuccs();
+ Instruction[] instr = block.getInstructions();
+ for (int i = 0; i < instr.length; i++) {
+ if (instr[i].hasLocal()) {
+ int slotlimit = instr[i].getLocalSlot() + 1;
+ int opcode = instr[i].getOpcode();
+ if (opcode == opc_lstore || opcode == opc_dstore
+ || opcode == opc_lload || opcode == opc_dload)
+ slotlimit++;
+ if (slotlimit > maxLocals)
+ maxLocals = slotlimit;
+ }
+ }
+ if (instr.length > 0
+ && instr[instr.length-1].getOpcode() == opc_jsr) {
+ if (!visited.get(succs[0].blockNr)) {
+ succs[0].stackHeight = stackHeight + 1;
+ todo.push(succs[0]);
+ visited.set(succs[0].blockNr);
+ } else if (succs[0].stackHeight != stackHeight + 1)
+ throw new IllegalArgumentException
+ ("Block has two different stack heights.");
+
+ if (succs[1] != null && !visited.get(succs[1].blockNr)) {
+ succs[1].stackHeight = stackHeight;
+ todo.push(succs[1]);
+ visited.set(succs[1].blockNr);
+ } else if ((succs[1] == null ? 0 : succs[1].stackHeight)
+ != stackHeight)
+ throw new IllegalArgumentException
+ ("Block has two different stack heights.");
+ } else {
+ for (int i = 0; i < succs.length; i++) {
+ if (succs[i] != null && !visited.get(succs[i].blockNr)) {
+ succs[i].stackHeight = stackHeight;
+ todo.push(succs[i]);
+ visited.set(succs[i].blockNr);
+ } else if ((succs[i] == null ? 0 : succs[i].stackHeight)
+ != stackHeight)
+ throw new IllegalArgumentException
+ ("Block has two different stack heights.");
+ }
+ }
+ Handler[] handler = block.getHandlers();
+ for (int i = 0; i < handler.length; i++) {
+ if (!visited.get(handler[i].getCatcher().blockNr)) {
+ handler[i].getCatcher().stackHeight = 1;
+ todo.push(handler[i].getCatcher());
+ visited.set(handler[i].getCatcher().blockNr);
+ } else if (handler[i].getCatcher().stackHeight != 1)
+ throw new IllegalArgumentException
+ ("Block has two different stack heights.");
+ }
+ }
}
public void setBlocks(Block[] blocks, Block startBlock,
@@ -173,15 +248,18 @@ public class BasicBlocks extends BinaryInfo {
for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getStart() == blocks[i])
activeHandlers.add(handlers[j]);
- if (handlers[j].getEnd() == blocks[i])
- activeHandlers.remove(handlers[j]);
}
if (activeHandlers.size() == 0)
blocks[i].catchers = Handler.EMPTY;
else
blocks[i].catchers =
(Handler[]) activeHandlers.toArray(Handler.EMPTY);
+ for (int j = 0; j < handlers.length; j++) {
+ if (handlers[j].getEnd() == blocks[i])
+ activeHandlers.remove(handlers[j]);
+ }
}
+ updateMaxStackLocals();
// TransformSubroutine.createSubroutineInfo(this);
}
@@ -212,7 +290,7 @@ public class BasicBlocks extends BinaryInfo {
dumpCode(GlobalOptions.err);
}
- void readAttribute(String name, int length, ConstantPool cp,
+ protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL
@@ -256,15 +334,17 @@ public class BasicBlocks extends BinaryInfo {
BasicBlockWriter bbw;
void prepareWriting(GrowableConstantPool gcp) {
bbw = new BasicBlockWriter(this, gcp);
+ prepareAttributes(gcp);
}
- int getKnownAttributeCount() {
- return bbw.getAttributeCount();
+ protected int getAttributeCount() {
+ return super.getAttributeCount() + bbw.getAttributeCount();
}
- void writeKnownAttributes(GrowableConstantPool gcp,
- DataOutputStream output)
+ protected void writeAttributes(GrowableConstantPool gcp,
+ DataOutputStream output)
throws IOException {
+ super.writeAttributes(gcp, output);
bbw.writeAttributes(gcp, output);
}
diff --git a/jode/src/net/sf/jode/bytecode/BinaryInfo.java b/jode/src/net/sf/jode/bytecode/BinaryInfo.java
index 8989219..9ffa1fd 100644
--- a/jode/src/net/sf/jode/bytecode/BinaryInfo.java
+++ b/jode/src/net/sf/jode/bytecode/BinaryInfo.java
@@ -45,8 +45,15 @@ import java.util.Iterator;
* this package as appropriate. This methods are only useful for non
* standard attributes.
*
- *
One application of this attributes are installation classes.
- * These classes have a special attribute containing a zip of the
+ *
You can provide new attributes by overriding the protected
+ * methods of this class. This makes it possible to use constant pool
+ * entries in the attributes.
+ *
+ *
Another possibility is to add the attributes with the public
+ * method. This way you don't need to extend the classes, but you
+ * can't use a constant pool for the contents of the attributes. One
+ * possible application of this are installation classes. These
+ * classes have a special attribute containing a zip archive of the
* files that should be installed. There are other possible uses,
* e.g. putting native machine code for some architectures into the
* class.
@@ -70,14 +77,22 @@ public class BinaryInfo {
}
}
- int getKnownAttributeCount() {
- return 0;
- }
-
- void readAttribute(String name, int length,
- ConstantPool constantPool,
- DataInputStream input,
- int howMuch) throws IOException {
+ /**
+ * Reads in an attributes of this class. Overwrite this method if
+ * you want to handle your own attributes. If you don't know how
+ * to handle an attribute call this method for the super class.
+ * @param name the attribute name.
+ * @param length the length of the attribute.
+ * @param constantPool the constant pool of the class.
+ * @param input a data input stream where you can read the attribute
+ * from. It will protect you to read more over the attribute boundary.
+ * @param howMuch the constant that was given to the {@link
+ * ClassInfo#load} function when loading this class.
+ */
+ protected void readAttribute(String name, int length,
+ ConstantPool constantPool,
+ DataInputStream input,
+ int howMuch) throws IOException {
byte[] data = new byte[length];
input.readFully(data);
if (howMuch >= ClassInfo.ALL) {
@@ -156,12 +171,37 @@ public class BinaryInfo {
}
}
- void drop(int keep) {
+ /**
+ * Drops information from this info. Override this to drop your
+ * own info and don't forget to call the method of the super class.
+ * @param howMuch the constant that was given to the {@link
+ * ClassInfo#drop} function when loading this class.
+ */
+ protected void drop(int keep) {
if (keep < ClassInfo.ALL)
unknownAttributes = null;
}
- void prepareAttributes(GrowableConstantPool gcp) {
+ /**
+ * Returns the number of attributes of this class. Overwrite this
+ * method if you want to add your own attributes by providing a
+ * writeAttributes method. You should call this method for the
+ * super class and add the number of your own attributes to the
+ * returned value.
+ * @return the number of attributes of this class.
+ */
+ protected int getAttributeCount() {
+ return unknownAttributes != null ? unknownAttributes.size() : 0;
+ }
+
+ /**
+ * Prepare writing your attributes. Overwrite this method if you
+ * want to add your own attributes, which need constants on the
+ * class pool. Add the necessary constants to the constant pool
+ * and call this method for the super class.
+ * @param gcp The growable constant pool.
+ */
+ protected void prepareAttributes(GrowableConstantPool gcp) {
if (unknownAttributes == null)
return;
Iterator i = unknownAttributes.keySet().iterator();
@@ -169,19 +209,27 @@ public class BinaryInfo {
gcp.putUTF8((String) i.next());
}
- void writeKnownAttributes
- (GrowableConstantPool constantPool,
- DataOutputStream output) throws IOException {
- }
-
- void writeAttributes
+ /**
+ *
Writes the attributes to the output stream.
+ * Overwrite this method if you want to add your own attributes.
+ * All constants you need from the growable constant pool must
+ * have been previously registered by the {@link #prepareAttributes}
+ * method.
+ *
+ * First call the method of the super class. Afterwrites write
+ * each of your own attributes including the attribute header
+ * (name and length entry).
+ *
+ * @param gcp The growable constant pool, which is not growable anymore.
+ * @param output the data output stream. You must write exactly
+ * as many bytes to it as you have told with the {@link
+ * #getAttributeSize} method.
+ */
+ protected void writeAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
- int count = getKnownAttributeCount();
- if (unknownAttributes != null)
- count += unknownAttributes.size();
+ int count = getAttributeCount();
output.writeShort(count);
- writeKnownAttributes(constantPool, output);
if (unknownAttributes != null) {
Iterator i = unknownAttributes.entrySet().iterator();
while (i.hasNext()) {
@@ -195,7 +243,19 @@ public class BinaryInfo {
}
}
- int getAttributeSize() {
+ /**
+ * Gets the total length of all attributes in this binary info.
+ * Overwrite this method if you want to add your own attributes
+ * and add the size of your attributes to the value returned by
+ * the super class.
+ *
+ * Currently you only need to write this if you extend
+ * BasicBlocks.
+ *
+ * @return the total length of all attributes, including their
+ * headers and the number of attributes field.
+ */
+ protected int getAttributeSize() {
int size = 2; /* attribute count */
if (unknownAttributes != null) {
Iterator i = unknownAttributes.values().iterator();
@@ -206,9 +266,12 @@ public class BinaryInfo {
}
/**
- * Finds a non standard attribute with the given name.
+ * 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.
* @param name the name of the attribute.
* @return the contents of the attribute, null if not found.
+ * @see #readAttribute
*/
public byte[] findAttribute(String name) {
if (unknownAttributes != null)
@@ -217,7 +280,10 @@ public class BinaryInfo {
}
/**
- * Gets all non standard attributes
+ * 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.
+ * @see #findAttribute
*/
public Iterator getAttributes() {
if (unknownAttributes != null)
@@ -226,19 +292,24 @@ public class BinaryInfo {
}
/**
- * Adds a new non standard attribute or replaces an old one with the
- * same name.
+ * Adds a new non standard attribute or replaces an old one with
+ * the same name. If it already exists, it will be overwritten.
+ * Note that there's now way to correlate the contents with a
+ * constant pool. If you need that extend this class and override
+ * the methods {@link #getAttributeCount}, {@link
+ * #prepareAttributes}, {@link #writeAttributes}, and {@link
+ * #getAttributeSize}.
* @param name the name of the attribute.
* @param contents the new contens.
*/
- public void setAttribute(String name, byte[] contents) {
+ public void addAttribute(String name, byte[] contents) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, contents);
}
/**
- * Removes a new non standard attribute.
+ * Removes a non standard attributes.
* @param name the name of the attribute.
* @return the old contents of the attribute.
*/
@@ -249,11 +320,9 @@ public class BinaryInfo {
}
/**
- * Removes all non standard attribute.
+ * Removes all non standard attributes.
*/
public void removeAllAttributes() {
unknownAttributes = null;
}
}
-
-
diff --git a/jode/src/net/sf/jode/bytecode/Block.java b/jode/src/net/sf/jode/bytecode/Block.java
index 3337aee..88de266 100644
--- a/jode/src/net/sf/jode/bytecode/Block.java
+++ b/jode/src/net/sf/jode/bytecode/Block.java
@@ -92,28 +92,34 @@ public final class Block {
int blockNr;
/**
- * The blockNr of this block. Set by BasicBlocks.
+ * The number of items this block takes from the stack with
+ * respect to the stack items at the beginning of the block.
*/
- int lineNr;
-
+ int maxpop;
/**
- * The number of items this block takes from the stack.
+ * The maximum number of items the stack may grow.
*/
- int pop;
+ int maxpush;
/**
- * The number of items this block puts on the stack.
+ * The difference stack items after the block minus stack items
+ * before block.
*/
- int push;
-
+ int delta;
+ /**
+ * The stack height at the beginning of this block.
+ * Only valid after the block was inserted in a BasicBlocks and
+ * the updateMaxStackLocals() of BasicBlocks was called.
+ */
+ int stackHeight;
/**
- * Creates a new empty block, with a null successor array.
+ * Creates a new block uninitialized block. You mustn't really
+ * use it (except as successor for other blocks) until you have
+ * set the code.
*/
public Block() {
- instrs = new Instruction[0];
- succs = null;
}
-
+
/**
* Gets the list of instructions. The returned list should not be
* modified, except that the instructions (but not their opcodes)
@@ -134,12 +140,13 @@ public final class Block {
}
/**
- * Gets the exception handlers which try part contains this block.
- * You can't set them since they are calculated automatically.
+ * Gets the exception handlers whose try region contains this
+ * block. You can't set them since they are calculated
+ * automatically.
* @return the exception handlers.
- * @see BasicBlocks#setExceptionHandlers
+ * @see BasicBlocks#setBlocks
*/
- public Handler[] getCatchers() {
+ public Handler[] getHandlers() {
return catchers;
}
@@ -158,15 +165,18 @@ public final class Block {
private void initCode() {
int size = instrs.length;
+ maxpop = maxpush = 0;
int depth = 0;
int poppush[] = new int[2];
boolean needGoto = true;
for (int i = 0; i < size; i++) {
instrs[i].getStackPopPush(poppush);
- depth += poppush[0];
- if (pop < depth)
- pop = depth;
- depth -= poppush[1];
+ depth -= poppush[0];
+ if (maxpop < -depth)
+ maxpop = -depth;
+ depth += poppush[1];
+ if (maxpush < depth)
+ maxpush = depth;
int opcode = instrs[i].getOpcode();
switch (opcode) {
@@ -176,7 +186,7 @@ public final class Block {
case Opcodes.opc_lookupswitch:
if (succs.length != instrs[i].getValues().length + 1)
throw new IllegalArgumentException
- ("no successors for switch");
+ ("number of successors for switch doesn't match");
if (i != size - 1)
throw new IllegalArgumentException
("switch in the middle!");
@@ -217,14 +227,14 @@ public final class Block {
needGoto = false;
}
}
- push = pop - depth;
+ delta = depth;
if (needGoto && succs.length != 1)
throw new IllegalArgumentException("no single successor block");
}
public void getStackPopPush (int[] poppush) {
- poppush[0] = pop;
- poppush[1] = push;
+ poppush[0] = maxpop;
+ poppush[1] = delta + maxpop;
return;
}
@@ -235,12 +245,7 @@ public final class Block {
public void setCode(Instruction[] instrs, Block[] succs) {
this.instrs = instrs;
this.succs = succs;
- try {
- initCode();
- } catch (IllegalArgumentException ex) {
- dumpCode(net.sf.jode.GlobalOptions.err);
- throw ex;
- }
+ initCode();
}
public void dumpCode(PrintWriter output) {
diff --git a/jode/src/net/sf/jode/bytecode/ClassInfo.java b/jode/src/net/sf/jode/bytecode/ClassInfo.java
index b8fb9fd..f3dd540 100644
--- a/jode/src/net/sf/jode/bytecode/ClassInfo.java
+++ b/jode/src/net/sf/jode/bytecode/ClassInfo.java
@@ -59,13 +59,13 @@ import java.lang.reflect.Modifier;
* now have two different possibilities to fill it with informations:
* You load the class from its classpath (from which it was created)
* or you build it from scratch by setting its contents with the
- * various setXXX methods.
+ * various setSomething methods.
*
*
Changing a class
* Even if the class is already filled with information you can change
* it. You can, for example, set another array of methods, change the
* modifiers, or rename the class. Use the various
- * setXXX methods.
+ * setSomething methods.
*
*
The components of a class
* A class consists of several components:
@@ -79,7 +79,7 @@ import java.lang.reflect.Modifier;
*
class name
* The short java name of this class, i.e. the name that appears
* behind the "class" keyword in the java source file. While
- * getClassName() also works for top level classes,
+ * getClassName() also works for package scope classes,
* setClassName() must only be called on inner classes and will not
* change the bytecode name.
*
@@ -121,22 +121,17 @@ import java.lang.reflect.Modifier;
* Fields are represented as {@link MethodInfo} objects.
*
*
method scoped
- * True if this class is an anonymous or method scoped class.
+ * A boolean value; true if this class is an anonymous or method
+ * scoped class.
*
*
outer class
- * the class of which this class is the inner. It returns null for
- * top level classes and for method scoped classes.
+ * the class of which this class is the inner class. It returns
+ * null for package scoped and method scoped classes.
*
*
classes
* the inner classes which is an array of ClassInfo. This doesn't
* include method scoped classes.
*
- *
extra classes
- * this field tells only, for which additional classes the class
- * files contains an InnerClass Attribute. This can be the method
- * scoped classes or the inner inner classes, but there is no
- * promise.
- *
*
source file
* The name of source file. The JVM uses this field when a stack
* trace is produced.
@@ -155,16 +150,16 @@ import java.lang.reflect.Modifier;
* getOuterClass() returns null and
* isMethodScoped() returns false.
*
- *
inner class scoped classes
+ *
class scoped classes (inner classes)
* A class is class scoped if, and only if
* getOuterClass() returns not null.
*
* The bytecode name (getName()) of an inner class is
- * most times of the form Package.Outer$Inner. But
+ * in most cases of the form Package.Outer$Inner. But
* ClassInfo also supports differently named classes, as long as the
* InnerClass attribute is present. The method
* getClassName() returns the name of the inner class
- * (Inner in the above example).
+ * (Inner in the above example).
*
* You can get all inner classes of a class with the
* getClasses method.
@@ -177,12 +172,13 @@ import java.lang.reflect.Modifier;
* too.
*
* The bytecode name (getName()) of an method scoped class is
- * most times of the form Package.Outer$Number$Inner. But
+ * in most cases of the form Package.Outer$Number$Inner. But
* ClassInfo also supports differently named classes, as long as the
* InnerClass attribute is present.
*
* There's no way to get the method scoped classes of a method, except
- * by analyzing its instructions.
+ * by analyzing its instructions. And even that is error prone, since
+ * it could just be a method scoped class of an outer method.
*
*
anonymous classes
* A class is an anonymous class if, and only if
@@ -191,18 +187,20 @@ import java.lang.reflect.Modifier;
* case getOuterClass() returns null,
* too.
*
- * The bytecode name (getName()) of an method scoped class is
- * most times of the form Package.Outer$Number. But
- * ClassInfo also supports differently named classes, as long as the
- * InnerClass attribute is present.
+ * The bytecode name (getName()) of an method scoped
+ * class is in most cases of the form
+ * Package.Outer$Number. But ClassInfo also supports
+ * differently named classes, as long as the InnerClass attribute is
+ * present.
*
* There's no way to get the anonymous classes of a method, except
- * by analyzing its instructions.
+ * by analyzing its instructions. And even that is error prone, since
+ * it could just be an anonymous class of an outer method.
*
*
*
*
- *
open questions...
+ *
Open Questions
*
* I represent most types as java/lang/String (type
* signatures); this is convenient since java bytecode does the same.
@@ -211,17 +209,13 @@ import java.lang.reflect.Modifier;
* method to convert to it, but I need a ClassPath for this. Should
* the method be in ClassInfo (I don't think so), should an instance
* of TypeSignature have a ClassPath as member variable, or should
- * getClassInfo() take a ClassPath parameter? What about arrays?
+ * getClassInfo() take a ClassPath parameter as it is currently?
+ * What about arrays, shall we support special ClassInfo's for them,
+ * as java.lang.Class does? I think the current solution is okay.
*
*
- * Should load(HIERARCHY) also try to load hierarchy of super class.
- * If yes, should it return an IOException if super class can't be
- * found? Or should getSuperclass throw the IOException
- * instead (I don't like that).
- * Current Solution:superClassOf and
- * implementedBy can throw an IOException, getSuperclass not.
- *
- * @author Jochen Hoenicke */
+ * @author Jochen Hoenicke
+ */
public final class ClassInfo extends BinaryInfo implements Comparable {
private static ClassPath defaultClasspath;
@@ -245,6 +239,8 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private String sourceFile;
private boolean hasInnerClassesAttr;
+ private final static ClassInfo[] EMPTY_INNER = new ClassInfo[0];
+
/**
* This constant can be used as parameter to drop. It specifies
* that no information at all should be kept for the current class.
@@ -419,10 +415,24 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (status >= OUTERCLASS) {
if ((className == null
? this.className != null : !className.equals(this.className))
- || this.outerClass != outer)
- throw new ClassFormatException("Outer information mismatch "
- +name+": "+className+","+outer+","+ms+"<->"+this.className +","+this.outerClass+","+this.methodScoped);
- mergeModifiers(realModifiers);
+ || this.outerClass != outer) {
+ /* Ignore errors when merging, some obfuscator may have
+ * stripped InnerClasses attributes
+ */
+ if (this.className == null && this.outerClass == null
+ && (className != null || outer != null)) {
+ this.outerClass = outer;
+ this.className = className;
+ this.methodScoped = ms;
+ } else if (className != null || outer != null) {
+ GlobalOptions.err.println
+ ("WARNING: Outer information mismatch "
+ +name+": "+className+","+outer+","+ms+"<->"
+ +this.className +","+this.outerClass+","+this.methodScoped);
+ }
+ }
+ if (realModifiers != -1)
+ mergeModifiers(realModifiers);
} else {
if (realModifiers != -1)
mergeModifiers(realModifiers);
@@ -438,37 +448,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throws IOException
{
/* The InnerClasses attribute is transformed in a special way
- * so we want to taker a closer look. According to the inner
- * class specification,
- *
- * http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814
- *
- * there are several InnerClass records in a class. We differ
- * three different types:
- *
- * The InnerClass records for our own inner classes: They can
- * easily be recognized, since this class must be mentioned in
- * the outer_class_info_index field.
- *
- * The InnerClass records for the outer class and its outer
- * classes, and so on: According to the spec, these must
- * always be present if this is a class scoped class. And they
- * must be in reversed order, i.e. the outer most class comes
- * first.
+ * so we want to taker a closer look. According to the 2nd
+ * edition of the vm specification (InnerClasses attribute),
*
- * Some other InnerClass records, the extra classes. This is
- * optional, but we don't want to loose this information if we
- * just transform classes, so we memorize for which classes we
- * have to keep the information anyway.
+ * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996
*
- * Currently we don't use all informations, since we don't
- * update the information for inner/outer/extra classes or
- * check it for consistency. This might be bad, since this
- * means that
- *
- * load(ALL); write(new File())
- *
- * doesn't work.
+ * there is a InnerClass record for each non package scope
+ * class referenced in this class. We are only interested in
+ * out own entry and in the entries for our inner classes.
+ * The latter can easily be recognized, since this class must
+ * be mentioned in the outer_class_info_index field.
*/
hasInnerClassesAttr = true;
@@ -478,14 +467,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throw new ClassFormatException
("InnerClasses attribute has wrong length");
- int innerCount = 0, outerCount = 0, extraCount = 0;
+ int innerCount = 0;
/**
* The first part will contain the inner classes, the last
* part the extra classes.
*/
- ClassInfo[] innerExtra = new ClassInfo[count];
+ ClassInfo[] innerCIs = new ClassInfo[count];
- for (int i=0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
int innerIndex = input.readUnsignedShort();
int outerIndex = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort();
@@ -497,12 +486,17 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (innername != null && innername.length() == 0)
innername = null;
- /* Some compilers give method scope classes a valid
- * outer field, but we mustn't handle them as inner
- * classes. The best way to distinguish this case
- * is by the class name.
+ /* Some compilers give method scope and anonymous classes
+ * a valid outer field, but we mustn't handle them as
+ * inner classes.
*/
- if (outer != null && innername != null
+ if (innername == null)
+ outer = null;
+
+ /* The best way to distinguish method scope classes is by thier
+ * class name.
+ */
+ if (outer != null
&& inner.length() > outer.length() + 2 + innername.length()
&& inner.startsWith(outer+"$")
&& inner.endsWith("$"+innername)
@@ -510,45 +504,13 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
outer = null;
ClassInfo innerCI = classpath.getClassInfo(inner);
- ClassInfo outerCI = null;
- if (outer != null) {
- outerCI = classpath.getClassInfo(outer);
- /* If we didn't find an InnerClasses info for outerCI, yet,
- * this means that it doesn't have an outer class. So we
- * know its (empty) outer class status now.
- */
- if (outerCI.status < OUTERCLASS)
- outerCI.mergeOuterInfo(null, null, -1, false);
- }
-
- if (innername == null)
- /* anonymous class */
- outerCI = null;
+ ClassInfo outerCI = outer != null
+ ? classpath.getClassInfo(outer) : null;
innerCI.mergeOuterInfo(innername, outerCI,
access, outerCI == null);
if (outerCI == this)
- innerExtra[innerCount++] = innerCI;
- else {
- /* Remove outerCI from the extra part of innerExtra
- * since it is implicit now.
- */
- for (int j = count - 1; j >= count - extraCount; j--) {
- if (innerExtra[j] == outerCI) {
- System.arraycopy(innerExtra, count - extraCount,
- innerExtra, count - extraCount + 1,
- j - (count - extraCount));
- extraCount--;
- break;
- }
- }
-
- /* Add innerCI to the extra classes, except if it is
- * this class.
- */
- if (innerCI != this)
- innerExtra[count - (++extraCount)] = innerCI;
- }
+ innerCIs[innerCount++] = innerCI;
}
/* Now inner classes are at the front of the array in correct
@@ -557,32 +519,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/
if (innerCount > 0) {
innerClasses = new ClassInfo[innerCount];
- for (int i=0; i < innerCount; i++)
- innerClasses[i] = innerExtra[i];
+ System.arraycopy(innerCIs, 0, innerClasses, 0, innerCount);
} else
- innerClasses = null;
-
- /* All remaining classes that are mentioned in the constant
- * pool must have an empty outer class info. This is
- * specified in the 2nd edition of the JVM specification.
- */
- for (int i = 1; i < cp.size(); i++) {
- if (cp.tags[i] == cp.CLASS) {
- String clName = cp.getUTF8(cp.indices1[i]);
- if (clName.charAt(0) != '[') {
- ClassInfo ci = classpath.getClassInfo
- (clName.replace('/','.'));
- if (ci.status < OUTERCLASS)
- ci.mergeOuterInfo(null, null, -1, false);
- }
- }
- }
+ innerClasses = EMPTY_INNER;
}
- void readAttribute(String name, int length,
- ConstantPool cp,
- DataInputStream input,
- int howMuch) throws IOException {
+ protected void readAttribute(String name, int length,
+ ConstantPool cp,
+ DataInputStream input,
+ int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) {
if (length != 2)
throw new ClassFormatException("SourceFile attribute"
@@ -642,7 +587,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
(clazz.getSuperclass().getName());
Class[] ifaces = clazz.getInterfaces();
interfaces = new ClassInfo[ifaces.length];
- for (int i=0; i= PUBLICDECLARATIONS) {
@@ -707,7 +652,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
innerClasses[i].loadFromReflection(is[i], OUTERCLASS);
}
} else
- innerClasses = null;
+ innerClasses = EMPTY_INNER;
}
status = howMuch;
}
@@ -761,11 +706,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
String className = cpool.getClassName(input.readUnsignedShort());
if (!name.equals(className))
throw new ClassFormatException("wrong name " + className);
- String superName = cpool.getClassName(input.readUnsignedShort());
- superclass = superName != null ? classpath.getClassInfo(superName) : null;
+ 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++) {
+ for (int i = 0; i < count; i++) {
interfaces[i] = classpath.getClassInfo
(cpool.getClassName(input.readUnsignedShort()));
}
@@ -775,14 +721,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort();
fields = new FieldInfo[count];
- for (int i=0; i< count; i++) {
+ 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++) {
+ for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
@@ -792,29 +738,45 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort();
methods = new MethodInfo[count];
- for (int i=0; i< count; i++) {
+ 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++) {
+ for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
+ /* initialize inner classes to empty array, in case there
+ * is no InnerClasses attribute.
+ */
+ innerClasses = EMPTY_INNER;
+
/* attributes */
readAttributes(cpool, input, howMuch);
+
+ /* All classes that are mentioned in the constant pool must
+ * have an empty outer class info. This is specified in the
+ * 2nd edition of the JVM specification.
+ */
+ Iterator iter = cpool.iterateClassNames();
+ while (iter.hasNext()) {
+ ClassInfo ci = classpath.getClassInfo((String) iter.next());
+ if (ci.status < OUTERCLASS)
+ ci.mergeOuterInfo(null, null, -1, false);
+ }
status = howMuch;
}
private void reserveSmallConstants(GrowableConstantPool gcp) {
- for (int i=0; i < fields.length; i++)
+ for (int i = 0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp);
- for (int i=0; i < methods.length; i++)
+ for (int i = 0; i < methods.length; i++)
methods[i].reserveSmallConstants(gcp);
}
@@ -823,16 +785,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name);
gcp.putClassName(superclass.name);
- for (int i=0; i < interfaces.length; i++)
+ for (int i = 0; i < interfaces.length; i++)
gcp.putClassName(interfaces[i].name);
- for (int i=0; i < fields.length; i++)
+ for (int i = 0; i < fields.length; i++)
fields[i].prepareWriting(gcp);
- for (int i=0; i < methods.length; i++)
+ for (int i = 0; i < methods.length; i++)
methods[i].prepareWriting(gcp);
- for (int i=0; i < innerClasses.length; i++)
+ for (int i = 0; i < innerClasses.length; i++)
gcp.putClassName(innerClasses[i].name);
if (sourceFile != null) {
@@ -845,40 +807,29 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* edition of the JVM specification.
*/
hasInnerClassesAttr = false;
- for (int i = 1; i < gcp.size(); i++) {
- if (gcp.tags[i] == gcp.CLASS) {
- String clName;
- try {
- clName = gcp.getUTF8(gcp.indices1[i]);
- } catch (ClassFormatException ex) {
- throw new InternalError(ex.getMessage());
- }
- if (clName.charAt(0) != '[') {
- ClassInfo ci = classpath.getClassInfo
- (clName.replace('/','.'));
- if (ci.status < OUTERCLASS) {
- GlobalOptions.err.println
- ("WARNING: "+ ci.name
- + "'s outer class isn't known.");
- } else {
- if ((ci.outerClass != null || ci.methodScoped)
- && ! hasInnerClassesAttr) {
- gcp.putUTF8("innerClasses");
- hasInnerClassesAttr = true;
- }
- if (ci.outerClass != null)
- gcp.putClassName(ci.outerClass.name);
- if (ci.className != null)
- gcp.putUTF8(ci.className);
- }
+ Iterator iter = gcp.iterateClassNames();
+ while (iter.hasNext()) {
+ ClassInfo ci = classpath.getClassInfo((String) iter.next());
+ if (ci.status < OUTERCLASS) {
+ GlobalOptions.err.println
+ ("WARNING: " + ci.name + "'s outer class isn't known.");
+ } else {
+ if ((ci.outerClass != null || ci.methodScoped)
+ && ! hasInnerClassesAttr) {
+ gcp.putUTF8("InnerClasses");
+ hasInnerClassesAttr = true;
}
+ if (ci.outerClass != null)
+ gcp.putClassName(ci.outerClass.name);
+ if (ci.className != null)
+ gcp.putUTF8(ci.className);
}
}
prepareAttributes(gcp);
}
- int getKnownAttributeCount() {
- int count = 0;
+ protected int getAttributeCount() {
+ int count = super.getAttributeCount();
if (sourceFile != null)
count++;
if (hasInnerClassesAttr)
@@ -886,9 +837,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return count;
}
- void writeKnownAttributes(GrowableConstantPool gcp,
- DataOutputStream output)
+ protected void writeAttributes(GrowableConstantPool gcp,
+ DataOutputStream output)
throws IOException {
+ super.writeAttributes(gcp, output);
if (sourceFile != null) {
output.writeShort(gcp.putUTF8("SourceFile"));
output.writeInt(2);
@@ -896,28 +848,19 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
List outers = new ArrayList();
- for (int i = 0; i < gcp.size(); i++) {
- if (gcp.tags[i] == gcp.CLASS) {
- String clName;
- try {
- clName = gcp.getUTF8(gcp.indices1[i]);
- } catch (ClassFormatException ex) {
- throw new InternalError(ex.getMessage());
- }
- if (clName.charAt(0) != '[') {
- ClassInfo ci = classpath.getClassInfo
- (clName.replace('/','.'));
- while (ci.status >= OUTERCLASS
- && ci.outerClass != null || ci.methodScoped) {
- /* Order is important so remove ci if it
- * already exists and add it to the end. This
- * way the outermost classes go to the end.
- */
- outers.remove(ci);
- outers.add(ci);
- ci = ci.outerClass;
- }
- }
+ Iterator iter = gcp.iterateClassNames();
+ while (iter.hasNext()) {
+ ClassInfo ci = classpath.getClassInfo((String) iter.next());
+ while (ci != null
+ && ci.status >= OUTERCLASS
+ && (ci.outerClass != null || ci.methodScoped)) {
+ /* Order is important so remove ci if it
+ * already exists and add it to the end. This
+ * way the outermost classes go to the end.
+ */
+ outers.remove(ci);
+ outers.add(ci);
+ ci = ci.outerClass;
}
}
if (hasInnerClassesAttr) {
@@ -926,9 +869,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
output.writeInt(2 + count * 8);
output.writeShort(count);
- ListIterator iter = outers.listIterator(count);
- while (iter.hasPrevious()) {
- ClassInfo ci = (ClassInfo) iter.previous();
+ ListIterator listiter = outers.listIterator(count);
+ while (listiter.hasPrevious()) {
+ ClassInfo ci = (ClassInfo) listiter.previous();
output.writeShort(gcp.putClassName(ci.name));
output.writeShort(ci.outerClass == null ? 0 :
@@ -951,7 +894,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* @exception IllegalStateException if not enough information is set.
*/
public void write(DataOutputStream out) throws IOException {
- if (status <= ALL)
+ if (status < ALL)
throw new IllegalStateException("state is "+status);
GrowableConstantPool gcp = new GrowableConstantPool();
@@ -967,15 +910,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
out.writeShort(gcp.putClassName(name));
out.writeShort(gcp.putClassName(superclass.getName()));
out.writeShort(interfaces.length);
- for (int i=0; i < interfaces.length; i++)
+ for (int i = 0; i < interfaces.length; i++)
out.writeShort(gcp.putClassName(interfaces[i].getName()));
out.writeShort(fields.length);
- for (int i=0; i < fields.length; i++)
+ for (int i = 0; i < fields.length; i++)
fields[i].write(gcp, out);
out.writeShort(methods.length);
- for (int i=0; i < methods.length; i++)
+ for (int i = 0; i < methods.length; i++)
methods[i].write(gcp, out);
writeAttributes(gcp, out);
@@ -1018,11 +961,6 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* Guess the contents of a class. It
* @param howMuch The amount of information that should be read, e.g.
* HIERARCHY.
- * @exception ClassFormatException if the file doesn't denote a
- * valid class.
- * @exception FileNotFoundException if class wasn't found in classpath.
- * @exception IOException if an io exception occured.
- * @exception IllegalStateException if this ClassInfo was modified.
* @see #OUTERCLASS
* @see #HIERARCHY
* @see #PUBLICDECLARATIONS
@@ -1074,7 +1012,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
methods = new MethodInfo[0];
fields = new FieldInfo[0];
- innerClasses = new ClassInfo[0];
+ innerClasses = EMPTY_INNER;
}
status = howMuch;
}
@@ -1117,9 +1055,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/
keep = DECLARATIONS;
- for (int i=0; i < fields.length; i++)
+ for (int i = 0; i < fields.length; i++)
fields[i].drop(keep);
- for (int i=0; i < methods.length; i++)
+ for (int i = 0; i < methods.length; i++)
methods[i].drop(keep);
}
@@ -1201,7 +1139,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public FieldInfo findField(String name, String typeSig) {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
- for (int i=0; i< fields.length; i++)
+ for (int i = 0; i < fields.length; i++)
if (fields[i].getName().equals(name)
&& fields[i].getType().equals(typeSig))
return fields[i];
@@ -1211,7 +1149,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public MethodInfo findMethod(String name, String typeSig) {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
- for (int i=0; i< methods.length; i++)
+ for (int i = 0; i < methods.length; i++)
if (methods[i].getName().equals(name)
&& methods[i].getType().equals(typeSig))
return methods[i];
@@ -1260,6 +1198,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return methodScoped;
}
+ /**
+ * Returns the inner classes declared in this class.
+ * This needs at least PUBLICDECLARATION information loaded.
+ * @return an array containing the inner classes, guaranteed != null.
+ * @exception IllegalStateException if PUBLICDECLARATIONS information
+ * wasn't loaded yet. */
public ClassInfo[] getClasses() {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
@@ -1270,53 +1214,73 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return sourceFile;
}
+ /**
+ * 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.
+ */
public void setName(String newName) {
+ /* The class name is used as index in the hash table. We have
+ * to update the class path and tell it about the name change.
+ */
+ classpath.renameClassInfo(this, newName);
name = newName.intern();
+ status = ALL;
modified = true;
}
public void setSuperclass(ClassInfo newSuper) {
superclass = newSuper;
+ status = ALL;
modified = true;
}
public void setInterfaces(ClassInfo[] newIfaces) {
interfaces = newIfaces;
+ status = ALL;
modified = true;
}
public void setModifiers(int newModifiers) {
modifiers = newModifiers;
+ status = ALL;
modified = true;
}
public void setMethods(MethodInfo[] mi) {
methods = mi;
+ status = ALL;
modified = true;
}
public void setFields(FieldInfo[] fi) {
fields = fi;
+ status = ALL;
modified = true;
}
public void setOuterClass(ClassInfo oc) {
outerClass = oc;
+ status = ALL;
modified = true;
}
public void setMethodScoped(boolean ms) {
methodScoped = ms;
+ status = ALL;
modified = true;
}
public void setClasses(ClassInfo[] ic) {
- innerClasses = ic;
+ innerClasses = ic.length == 0 ? EMPTY_INNER : ic;
+ status = ALL;
modified = true;
}
public void setSourceFile(String newSource) {
sourceFile = newSource;
+ status = ALL;
modified = true;
}
@@ -1363,12 +1327,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone();
Arrays.sort(interfaces);
- for (int i=0; i < interfaces.length; i++)
+ for (int i = 0; i < interfaces.length; i++)
out.writeUTF(interfaces[i].name);
FieldInfo[] fields = (FieldInfo[]) this.fields.clone();
Arrays.sort(fields);
- for (int i=0; i < fields.length; i++) {
+ for (int i = 0; i < fields.length; i++) {
modifs = fields[i].getModifiers();
if ((modifs & Modifier.PRIVATE) != 0
&& (modifs & (Modifier.STATIC
@@ -1383,7 +1347,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
MethodInfo[] methods = (MethodInfo[]) this.methods.clone();
Arrays.sort(methods);
- for (int i=0; i < methods.length; i++) {
+ for (int i = 0; i < methods.length; i++) {
modifs = methods[i].getModifiers();
/* The modifiers of should be just static,
* but jikes also marks it final.
@@ -1405,7 +1369,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
byte[] sha = md.digest();
long result = 0;
- for (int i=0; i < 8; i++) {
+ for (int i = 0; i < 8; i++) {
result += (long)(sha[i] & 0xFF) << (8 * i);
}
return result;
@@ -1461,7 +1425,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (clazz.status < HIERARCHY)
clazz.load(HIERARCHY);
ClassInfo[] ifaces = clazz.getInterfaces();
- for (int i=0; i< ifaces.length; i++) {
+ for (int i = 0; i < ifaces.length; i++) {
if (implementedBy(ifaces[i]))
return true;
}
diff --git a/jode/src/net/sf/jode/bytecode/ClassPath.java b/jode/src/net/sf/jode/bytecode/ClassPath.java
index 2ccea9e..b2f2af8 100644
--- a/jode/src/net/sf/jode/bytecode/ClassPath.java
+++ b/jode/src/net/sf/jode/bytecode/ClassPath.java
@@ -684,6 +684,26 @@ public class ClassPath {
return clazz;
}
+ /**
+ * Updates the classes unify hash for a class renaming. This
+ * should be only called by {@link ClassInfo#setName}.
+ */
+ void renameClassInfo(ClassInfo classInfo, String classname) {
+ classes.remove(classInfo.getName().hashCode(), classInfo);
+ /* Now remove any class already loaded with that name, just
+ * in case we're overwriting one.
+ */
+ 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);
+ }
+
/**
* Checks, if a class with the given name exists somewhere in this
* path.
@@ -752,7 +772,7 @@ public class ClassPath {
* @param fqn the full qualified name. The components should be dot
* separated.
* @return true, if filename exists and is a package, false otherwise.
- * @see isDirectory
+ * @see #isDirectory
*/
public boolean isPackage(String fqn) {
return isDirectory(fqn.replace('.', '/'));
diff --git a/jode/src/net/sf/jode/bytecode/ConstantPool.java b/jode/src/net/sf/jode/bytecode/ConstantPool.java
index 867b2b2..f90b7ce 100644
--- a/jode/src/net/sf/jode/bytecode/ConstantPool.java
+++ b/jode/src/net/sf/jode/bytecode/ConstantPool.java
@@ -21,12 +21,22 @@ package net.sf.jode.bytecode;
import java.io.DataInputStream;
import java.io.IOException;
+import java.util.NoSuchElementException;
+///#def COLLECTIONS java.util
+import java.util.Iterator;
+///#enddef
+///#def COLLECTIONEXTRA java.lang
+import java.lang.UnsupportedOperationException;
+///#enddef
+
/**
- * This class represent the constant pool.
+ * 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.
*
* @author Jochen Hoenicke
*/
-class ConstantPool {
+public class ConstantPool {
public final static int CLASS = 7;
public final static int FIELDREF = 9;
public final static int METHODREF = 10;
@@ -106,16 +116,12 @@ class ConstantPool {
}
public String getUTF8(int i) throws ClassFormatException {
- if (i == 0)
- return null;
if (tags[i] != UTF8)
throw new ClassFormatException("Tag mismatch");
return (String)constants[i];
}
public Reference getRef(int i) throws ClassFormatException {
- if (i == 0)
- return null;
if (tags[i] != FIELDREF
&& tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF)
throw new ClassFormatException("Tag mismatch");
@@ -156,8 +162,6 @@ class ConstantPool {
}
public String getClassType(int i) throws ClassFormatException {
- if (i == 0)
- return null;
if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]);
@@ -169,13 +173,10 @@ class ConstantPool {
} catch (IllegalArgumentException ex) {
throw new ClassFormatException(ex.getMessage());
}
-
return clName;
}
public String getClassName(int i) throws ClassFormatException {
- if (i == 0)
- return null;
if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]);
@@ -187,6 +188,43 @@ class ConstantPool {
return clName.replace('/','.').intern();
}
+ /**
+ * Iterates through all class entries in the class pool and returns
+ * their (dot seperated) class name.
+ */
+ public Iterator iterateClassNames() {
+ return new Iterator()
+ {
+ int entry = 1;
+ public boolean hasNext() {
+ try {
+ while (entry < count
+ && (tags[entry] != CLASS
+ || getUTF8(indices1[entry])
+ .charAt(0) == '['))
+ entry++;
+ } catch (ClassFormatException ex) {
+ throw new InternalError(ex.getMessage());
+ }
+ return entry < count;
+ }
+
+ public Object next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ try {
+ return getClassName(entry++);
+ } catch (ClassFormatException ex) {
+ throw new InternalError(ex.getMessage());
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
public String toString(int i) {
switch (tags[i]) {
case CLASS:
@@ -225,7 +263,7 @@ class ConstantPool {
}
public String toString() {
- StringBuffer result = new StringBuffer("[ null");
+ StringBuffer result = new StringBuffer("ConstantPool[ null");
for (int i=1; i< count; i++) {
result.append(", ").append(i).append(" = ").append(toString(i));
}
diff --git a/jode/src/net/sf/jode/bytecode/FieldInfo.java b/jode/src/net/sf/jode/bytecode/FieldInfo.java
index 82b5fdd..a257d00 100644
--- a/jode/src/net/sf/jode/bytecode/FieldInfo.java
+++ b/jode/src/net/sf/jode/bytecode/FieldInfo.java
@@ -44,10 +44,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
this.modifier = modifier;
}
- void readAttribute(String name, int length,
- ConstantPool cp,
- DataInputStream input,
- int howMuch) throws IOException {
+ protected void readAttribute(String name, int length,
+ ConstantPool cp,
+ DataInputStream input,
+ int howMuch) throws IOException {
if (howMuch >= ClassInfo.DECLARATIONS
&& name.equals("ConstantValue")) {
if (length != 2)
@@ -97,8 +97,8 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp);
}
- protected int getKnownAttributeCount() {
- int count = 0;
+ protected int getAttributeCount() {
+ int count = super.getAttributeCount();
if (constant != null)
count++;
if (syntheticFlag)
@@ -108,9 +108,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
return count;
}
- void writeKnownAttributes(GrowableConstantPool gcp,
- DataOutputStream output)
+ protected void writeAttributes(GrowableConstantPool gcp,
+ DataOutputStream output)
throws IOException {
+ super.writeAttributes(gcp, output);
if (constant != null) {
output.writeShort(gcp.putUTF8("ConstantValue"));
output.writeInt(2);
@@ -140,7 +141,7 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
writeAttributes(constantPool, output);
}
- void drop(int keep) {
+ protected void drop(int keep) {
if (keep < ClassInfo.DECLARATIONS)
constant = null;
super.drop(keep);
diff --git a/jode/src/net/sf/jode/bytecode/Handler.java b/jode/src/net/sf/jode/bytecode/Handler.java
index d67bd94..5367f06 100644
--- a/jode/src/net/sf/jode/bytecode/Handler.java
+++ b/jode/src/net/sf/jode/bytecode/Handler.java
@@ -66,7 +66,7 @@ public class Handler {
}
/**
- * Gets the type signature of the exception.
+ * Gets the class name of the exception type.
*/
public String getType() {
return type;
@@ -85,7 +85,7 @@ public class Handler {
}
/**
- * Sets the type signature of the exception.
+ * Sets the class name of the exception type.
*/
public void setType(String type) {
this.type = type;
diff --git a/jode/src/net/sf/jode/bytecode/MethodInfo.java b/jode/src/net/sf/jode/bytecode/MethodInfo.java
index 47ea39d..ec5ec0f 100644
--- a/jode/src/net/sf/jode/bytecode/MethodInfo.java
+++ b/jode/src/net/sf/jode/bytecode/MethodInfo.java
@@ -26,6 +26,38 @@ import java.lang.reflect.Modifier;
import java.lang.Comparable;
///#enddef
+/**
+ *
Represents a java bytecode method. A method consists of the following
+ * parts:
+ *
+ *
+ *
+ *
name
The method's name
+ *
+ *
type
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}.
+ *
+ *
basicblocks
the bytecode of the method in form of
+ * {@link BasicBlocks basic blocks}, null if it is native or
+ * abstract.
+ *
+ *
synthetic
true if this method is synthetic
+ *
+ *
deprecated
true if this method is deprecated
+ *
+ *
exceptions
the exceptions that the method declared in
+ * its throws clause
+ *
+ *
+ *
+ * @author Jochen Hoenicke
+ * @see net.sf.jode.bytecode.TypeSignature
+ * @see net.sf.jode.bytecode.BasicBlocks
+ */
public final class MethodInfo extends BinaryInfo implements Comparable {
int modifier;
String name;
@@ -45,8 +77,9 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
this.modifier = modifier;
}
- void readAttribute(String name, int length, ConstantPool cp,
- DataInputStream input, int howMuch) throws IOException {
+ protected void readAttribute
+ (String name, int length, ConstantPool cp,
+ DataInputStream input, int howMuch) throws IOException {
if (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) {
basicblocks = new BasicBlocks(this);
basicblocks.read(cp, input, howMuch);
@@ -105,8 +138,8 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp);
}
- int getKnownAttributeCount() {
- int count = 0;
+ protected int getAttributeCount() {
+ int count = super.getAttributeCount();
if (basicblocks != null)
count++;
if (exceptions != null)
@@ -118,9 +151,10 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
return count;
}
- void writeKnownAttributes(GrowableConstantPool gcp,
- DataOutputStream output)
+ protected void writeAttributes(GrowableConstantPool gcp,
+ DataOutputStream output)
throws IOException {
+ super.writeAttributes(gcp, output);
if (basicblocks != null) {
output.writeShort(gcp.putUTF8("Code"));
basicblocks.write(gcp, output);
@@ -144,14 +178,14 @@ public final class MethodInfo 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));
writeAttributes(constantPool, output);
}
- void drop(int keep) {
+ protected void drop(int keep) {
if (keep < ClassInfo.DECLARATIONS)
exceptions = null;
if (keep < ClassInfo.NODEBUG)
@@ -260,8 +294,12 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
return result;
}
+ /**
+ * Returns a string representation of this method. It consists
+ * of the method's name and type signature.
+ * @return a string representation of this method.
+ */
public String toString() {
- return "Method "+Modifier.toString(modifier)+" "+
- typeSig + " " + name;
+ return "MethodInfo[name="+name+",type="+typeSig+"]";
}
}
diff --git a/jode/src/net/sf/jode/bytecode/Subroutine.java b/jode/src/net/sf/jode/bytecode/Subroutine.java
deleted file mode 100644
index 5b91bbe..0000000
--- a/jode/src/net/sf/jode/bytecode/Subroutine.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Subroutine Copyright (C) 2000 Jochen Hoenicke.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id$
- */
-
-package net.sf.jode.bytecode;
-
-import java.io.PrintWriter;
-///#def COLLECTIONS java.util
-import java.util.Collection;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Iterator;
-///#enddef
-
-/**
- *
Represents a jsr-Subroutine.
- *
- *
In my representation a subroutine consists of all blocks from
- * which the ret instruction is reachable. Every subroutine must have
- * a reachable ret instruction, or the jsr is replaced by a simple goto.
- *
- *
- * @author Jochen Hoenicke
- * @see net.sf.jode.bytecode.BasicBlocks
- * @see net.sf.jode.bytecode.Block
- */
-public final class Subroutine {
- /**
- * Subroutines may be nested. This points to the outer subroutine
- * or to null if this doesn't have an outer.
- */
- private Subroutine outer;
-
- /**
- * Each subroutine has exactly one ret instruction, which is the
- * last instruction in the retBlock. The local of the ret
- * instruction must equal the local where the first instruction of
- * the subroutine stores to.
- */
- private Block retBlock;
-
- /**
- * The set of locals that are accessed inside this subroutine.
- */
- private BitSet accessedLocals;
-
- public Block getRetBlock() {
- return retBlock;
- }
-
- public Subroutine getOuter() {
- return outer;
- }
-
- public boolean isAccessed(int slot) {
- return accessedLocals.get(slot);
- }
-}
diff --git a/jode/src/net/sf/jode/bytecode/TransformSubroutine.java b/jode/src/net/sf/jode/bytecode/TransformSubroutine.java
deleted file mode 100644
index b5cb075..0000000
--- a/jode/src/net/sf/jode/bytecode/TransformSubroutine.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/* TransformSubroutine Copyright (C) 1999-2000 Jochen Hoenicke.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id$
- */
-
-package net.sf.jode.bytecode;
-import java.util.BitSet;
-
-/**
- *
This class contains some code to transform the subroutines of
- * a method into a normal form. The normal form is as following.
- *
- *
Each subroutine block, meaning a block where some jsr
- * instructions may jump to, must store the return address in a local
- * variable immediately. There must be exactly one block with the
- * corresponding opc_ret instruction and this block must
- * be reachable from all blocks that belong to this subroutine.
- *
- *
The JVM spec allows a subroutine, to leave the return address on
- * stack for a while, even longer than the subroutine actually exists.
- * One can also pop that value instead of storing it in a local
- * variable. And finally its possible to store it in a variable, but
- * there exists no reachable opc_ret instruction that returns
- * to that address.
- *
- *
If the return address is not used by the subroutine, we simply
- * replace the jsr by a jump and remove the pop/store instruction that
- * popped the return address.
- *
- *
If the return address is used, but not immediately stored, we simply
- * move the corresponding astore to the start of the subroutine.
- *
- * @see net.sf.jode.bytecode.Block
- * @see net.sf.jode.bytecode.Instruction
- */
-public class TransformSubroutine implements Opcodes {
- private final static int SUBSTATUS_SUBROUTINE = 1;
- private final static int SUBSTATUS_REMOVEDSUB = 2;
- private final static int SUBSTATUS_NOTSUB = 3;
- private final static int SUBSTATUS_INPROGRESS = 4;
-
- BasicBlocks bb;
- Blocks[] blocks;
- byte[] substatus;
- Subroutine[] subInfo;
-
-// {
-// for (int i=0; i < blocks.length; i++) {
-// Instructions[] instrs = blocks[i].getInstructions();
-// if (instrs[instrs.length-1].getOpcode() == opc_jsr) {
-// int srBlock = instrs.getSuccs()[0].getBlockNr();
-// if (substatus[srBlock] == 0)
-// analyzeSubroutine(srBlock);
-// if (substatus[srBlock] == SUBSTATUS_REMOVED) {
-// Instructions[] newInstrs
-// = new Instruction[instrs.length-1];
-// System.arraycopy(instrs, 0, newInstrs, 0,
-// newInstrs.length);
-// Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; };
-// blocks[i].setCode(newInstrs, newSuccs);
-// }
-// }
-// }
-// }
-
- class SubroutineInfo {
- int retPosition;
- BitSet accessedLocals;
- SubroutineInfo outer;
-
- SubroutineInfo(int retPos, Bitset accLocals, SubroutineInfo outer) {
- this.outer = outer;
- this.retPosition = retPos;
- this.accessedLocals = accLocals;
- }
-
- boolean merge(int retPos, BitSet accLocals, SubroutineInfo outer) {
- if ((retPos < 0 || this.retPosition < 0)
- && retPos != this.retPosition)
- throw new
- }
- }
-
- public TransformSubroutine(BasicBlocks bb)
- throws ClassFormatException
- {
- if (bb.getStartBlock() == null)
- return;
-
- blocks = bb.getBlocks();
- substatus = new byte[blocks.length];
- analyzeBlock(blockNr, SUBSTATUS_NOTSUB, null);
- }
-
- public void analyzeBlock(int blockNr, int status, SubroutineInfo outer,
- BitSet retsOnStack) {
- Block block = blocks[blockNr];
- if (status == SUBSTATUS_INPROGRESS) {
-
- }
-
- }
-
- public void analyzeBlock(int blockNr, int status, SubroutineInfo outer) {
- substatus[blockNr] = status;
- accessedLocals[blockNr] = accessed;
- Stack todo = new Stack();
-
- todo.add(new BlockInfo(startBlockNr, 0, null));
- while (!todo.isEmpty()) {
- BlockInfo info = todo.pop();
- Block block = blocks[info.blockNr];
- Instruction[] instrs = block.getInstructions();
- Instruction[] newInstrs = null;
- Block[] succs = block.getSuccessors();
-
-
- if (substatus[info.blockNr]
- == SUBSTATUS_INPROGRESS) {
- int retPosition = info.retPosition;
- BitSet =
- retPosition < 0 ? info.accessedLocals.clone() : null;
-
- for (int i = 0; i < instrs.length; i++) {
- Instruction instr = instrs[i];
- if (instr instanceof SlotInstruction) {
- if (instr.getOpcode() == opc_astore
- && retPosition == -1) {
-
- /* We found the store operation. At least
- * a candidate, since there may not be a
- * corresponding ret.
- */
-
- retPosition = instr.getLocalSlot();
- accessedLocals = null;
- /* remove store Instruction.
- */
- newInstrs = new Instruction[instrs.length - 1];
- System.arraycopy(instrs, 0, newInstrs, 0, i);
- System.arraycopy(instrs, i+1, newInstrs, i,
- newInstrs.length - i);
-
- } else {
- if (retPosition < 0) {
- accessedLocals.add(instr.getLocalSlot());
- switch (instr.getOpcode()) {
- case opc_lload:
- case opc_dload:
- case opc_lstore:
- case opc_dstore:
- accessedLocals.add(instr.getLocalSlot()+1);
- }
- }
- }
- } else if (instr.getOpcode() == opc_pop
- && retPosition == -1) {
- /* We spontanously left the subroutine by popping.
- * Remove the pop Instruction.
- */
- newInstrs = new Instruction[instrs.length - 1];
- System.arraycopy(instrs, 0, newInstrs, 0, i);
- System.arraycopy(instrs, i+1, newInstrs, i,
- newInstrs.length - i);
- substatus[info.blockNr] = SUBSTATUS_NOTSUB;
- break;
- } else if ((instr.getOpcode() == opc_pop2 &&
- (retPosition == -1 || retPosition == -2))) {
- /* We spontanously left the subroutine by popping.
- * Replace the pop2 with a pop.
- */
- newInstrs = new Instruction[instrs.length];
- System.arraycopy(instrs, 0, newInstrs, 0,
- instrs.length);
- newInstrs[i] = Instruction.forOpcode(opc_pop);
- substatus[info.blockNr] = SUBSTATUS_NOTSUB;
- break;
- } else if (instr.getOpcode() == opc_jsr) {
- /* A recursive subroutine (or have we already
- * spontanously left this subroutine?)
- */
- int srBlock = instrs.getSuccs()[0].getBlockNr();
- if (substatus[srBlock] == 0)
- analyzeSubroutine(srBlock);
- if (substatus[srBlock] == SUBSTATUS_INPROGRESS) {
- /* We spontanously left this subroutine! */
- if (retPosition < 0)
- /* This can't happen in valid code.
- */
- throw new CodeFormatException
- ("Can't merge return instr on Stack.");
- substatus[info.blockNr] = SUBSTATUS_NOTSUB;
- }
- leftSub = true;
- break;
- } else if (substatus[srBlock] == SUBSTATUS_REMOVED) {
- Instructions[] newInstrs
- = new Instruction[instrs.length-1];
- System.arraycopy(instrs, 0, newInstrs, 0,
- newInstrs.length);
- Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; };
- blocks[i].setCode(newInstrs, newSuccs);
- }
- }
- }
- if (!leftSub) {
-
-
- }
- }
- }
-}
-
-
diff --git a/jode/src/net/sf/jode/bytecode/package.html b/jode/src/net/sf/jode/bytecode/package.html
index 64409b9..758de1d 100644
--- a/jode/src/net/sf/jode/bytecode/package.html
+++ b/jode/src/net/sf/jode/bytecode/package.html
@@ -18,7 +18,7 @@
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-->
- JODE Bytecode Package
+JODE Bytecode Package
@@ -29,13 +29,9 @@ class and get a ClassInfo object. As third step you can actually load
the class.
Please notify me if you want to use this package. I will inform you
-about updates, help you with problems, etc. WARNING: This
-package may change in the future in incompatible ways. Ask me for
-more information.
-
-BIG FAT WARNING: Some of the things documented here aren't even
-implemented. Some of this is only vapor ware. But feel free to
-comment on this and point me to design errors!
+about updates, help you with problems, etc. WARNING: Some
+parts of this package may change in the future in incompatible ways.
+Ask me for more information.
Here is a short example, how you can use this package, see the
documentation of the classes for more details.
@@ -55,7 +51,7 @@ documentation of the classes for more details.
}
MethodInfo[] methods = clazz.getMethods();
- for (int i=0; i< methods.length; i++) {
+ for (int i = 0; i < methods.length; i++) {
String type = methods[i].getType();
if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE)
System.out.println("Found integer method: "+method.getName());
@@ -79,7 +75,7 @@ You can also use this package to create and write new classes:
Jochen Hoenicke
-Last modified: Mon Jun 26 09:46:24 MET DST 2000
+Last modified: Sun Aug 5 17:53:03 MEST 2001