Applied more patches from Jode-1.1 branch

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1329 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 24 years ago
parent 676e21257f
commit e78e8b0472
  1. 117
      jode/ChangeLog
  2. 13
      jode/INSTALL
  3. 4
      jode/Makefile.am
  4. 9
      jode/NEWS
  5. 97
      jode/README
  6. 5
      jode/TODO
  7. 12
      jode/configure.in
  8. 1
      jode/doc/.cvsignore
  9. 163
      jode/jode/decompiler/ClassAnalyzer.java
  10. 6
      jode/jode/decompiler/Decompiler.java
  11. 4
      jode/jode/decompiler/FieldAnalyzer.java
  12. 18
      jode/jode/decompiler/ImportHandler.java
  13. 3
      jode/jode/decompiler/LocalInfo.java
  14. 22
      jode/jode/decompiler/Main.java
  15. 41
      jode/jode/decompiler/MethodAnalyzer.java
  16. 20
      jode/jode/decompiler/OuterValues.java
  17. 15
      jode/jode/decompiler/TabbedPrintWriter.java
  18. 25
      jode/jode/expr/ArrayStoreOperator.java
  19. 2
      jode/jode/expr/ConstOperator.java
  20. 9
      jode/jode/expr/ConstantArrayOperator.java
  21. 2
      jode/jode/expr/Expression.java
  22. 77
      jode/jode/expr/FieldOperator.java
  23. 151
      jode/jode/expr/InvokeOperator.java
  24. 6
      jode/jode/flow/CaseBlock.java
  25. 2
      jode/jode/flow/InstructionBlock.java
  26. 9
      jode/jode/flow/JsrBlock.java
  27. 3
      jode/jode/flow/LoopBlock.java
  28. 1
      jode/jode/flow/Makefile.am
  29. 6
      jode/jode/flow/SpecialBlock.java
  30. 36
      jode/jode/flow/TransformConstructors.java
  31. 57
      jode/jode/flow/TransformExceptionHandlers.java
  32. 10
      jode/jode/jvm/CodeVerifier.java
  33. 85
      jode/jode/jvm/SyntheticAnalyzer.java
  34. 4
      jode/jode/obfuscator/modules/ConstantAnalyzer.java
  35. 18
      jode/jode/swingui/Main.java
  36. 9
      jode/jode/swingui/Resources.properties
  37. 9
      jode/jode/swingui/Resources_de.properties
  38. 27
      jode/jode/type/ArrayType.java
  39. 65
      jode/jode/type/ClassType.java
  40. 13
      jode/jode/type/RangeType.java

@ -1,3 +1,118 @@
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
* jode/decompiler/Decompiler.java (decompile): removed
setClassPath call. ClassInfo.forName() is no longer used.
* jode/decompiler/Main.java (decompile): likewise.
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patches from 2001-05-26 of Jode 1.1 tree:
* configure.in: Set version to 1.1.
* jode/swingui/Main.java (main): Also use bootclasspath if no
classpath given.
* jode/decompiler/MethodAnalyzer.java (skipWriting): Don't skip
empty constructor that have a throws clause.
* configure.in: Determine whether jdk1.1 resp. jdk1.2. Call jcpp
in config.status.
* jode/expr/Expression.java (makeInitializer): Now takes the
type of the initialization. Changed all callers.
* jode/expr/ConstantArrayOperator.java (makeInitializer): Check
that type is our array type, otherwise we can't omit new Array[].
* jode/decompiler/LocalInfo.java (markFinal): Don't check that
only one write is present. If two writes are in an then and an
else branch of an if, the local can still be final.
* jode/type/ArrayType.java (getSubType): Handle array of integer
types correctly: byte[] is something completely different than
int[].
(getSuperType): Likewise.
* jode/expr/FieldOperator.java (getFieldInfo): New function.
(needsCast): A cast is also needed if the field is private or
package scope and the current type can't access the field.
* jode/expr/InvokeOperator.java (getMethodInfo): New function.
(needsCast): A cast is also needed if the method is private or
package scope and the current type can't access the method.
* jode/expr/ArrayStoreOperator.java (dumpExpression): Check if a
cast of the array expression is needed.
* jode/expr/TransformConstructors.java
(transformFieldInitializers): Don't allow moving method invocations
that throw a checked exception.
* jode/bytecode/MethodInfo.java (readAttribute): Read Exceptions
attribute even when not all attributes should be read. They are
needed by TransformConstructors, see above.
* jode/decompiler/TabbedPrintWriter.java (saveOps): Don't allow
line breaks in not completed expressions since implicit parentheses
would destroy the syntax. No need to put line break option on stack.
(restoreOps): Adapted Stack format.
* jode/decompiler/ClassAnalyzer.java (dumpDeclaration): Moved
Code from dumpSource here. Don't put a line break after closing
brace.
(dumpSource): call dumpDeclaration and add a line break.
(dumpBlock): Moved dropInfo(ATTRIBS) here.
* jode/decompiler/ClassAnalyzer.java (STRICTFP): New Constant.
(isStrictFP): New function.
(initialize): Set strictfp modifier if a constructor has it set.
(dumpSource): Handle strictfp modifier.
* jode/decompiler/MethodAnalyzer.java (STRICTFP): New Constant.
(isStrictFP): New function.
(dumpSource): Handle strictfp modifier.
* jode/jvm/SyntheticAnalyzer.java (checkAccess): Check for a
special putfield access, where the set value is returned. Allow
the modifier of field/method to be protected and the class to be
a superclass.
(checkStaticAccess): Likewise.
(ACCESSDUPPUTFIELD): New Constant.
(ACCESSDUPPUTSTATIC): New Constant.
* jode/expr/InvokeOperator.java (simplifyAccess): Handle new
synthetics.
* jode/flow/SpecialBlock.java (removePop): Remove pop also for
non void store instructions.
* jode/decompiler/MethodAnalyzer.java (skipWriting): Also skip
the new synthetics.
* jode/decompiler/Main.java (main): Call System.exit() after
everything was compiled.
* jode/flow/TransformExceptionHandlers.java (removeJSR):
Renamed back from removeBadJSR (see patch from 2001-02-04). The
checkAndRemove* functions mustn't change the successors while they
iterate over them. Instead of removing good jsr they mark them as
good and removeJSR will finally remove them.
(checkAndRemoveJSR): See above.
(checkAndRemoveMonitorExit): See above.
* jode/flow/JsrBlock.java (good): New variable, see above.
(setGood): New method.
(isGood): New method.
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
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
understood why it is necessary. But see JVM Spec 4.9.4.
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-05-02 of Jode 1.1 tree:
* jode/obfuscator/modules/ConstantAnalyzer.java (handleOpcode):
Added divide by zero checks for opc_irem and opc_lrem.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patches from 2001-02-27 of Jode 1.1 tree: Applied patches from 2001-02-27 of Jode 1.1 tree:
@ -6,7 +121,7 @@
correct anyway. correct anyway.
Reported by Erik Modén. Reported by Erik Modén.
* jode/swingui/Main.java.in (AreaWriter): Convert all kinds of * jode/swingui/Main.java (AreaWriter): Convert all kinds of
line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea
understands. understands.

@ -1,8 +1,7 @@
Before installing, make sure you have a java compiler (e.g javac or Before installing, make sure you have at least version 1.1 of the java
jikes) and the java 1.1 runtime class library installed. If you want developement kit installed. If you want to run this program you only
to run this program you need at least a 1.1 compatible java virtual need the java runtime environment. Version 1.1 is quite old, I
machine. There are some bugs in javac included in the SUN JDK 1.1, it recommend using Java 2 (jdk1.2 or above).
won't work.
This package was designed to use the GNU standard for configuration This package was designed to use the GNU standard for configuration
and makefiles. To build and install do the following: and makefiles. To build and install do the following:
@ -44,7 +43,3 @@ give a path to the directory where it resides, otherwise it is
searched in the path. searched in the path.
3). Type "make" to build the package. 3). Type "make" to build the package.
4). Type "make install" to install the package. This doesn't work yet.
Jochen

@ -1,5 +1,3 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
SUBDIRS = jode bin doc test SUBDIRS = bin doc jode test
EXTRA_DIST = TODO

@ -1,6 +1,11 @@
New in 1.0.94 New in 1.2
* New bytecode interface
* Faster and better flow analyzation
New in 1.1
* break long lines * break long lines
* small bug fixes * handle most of javac v8 constructs (jdk 1.3)
* bug fixes
New in 1.0.93 New in 1.0.93
* anonymous and inner class decompilation reworked. * anonymous and inner class decompilation reworked.

@ -1,12 +1,17 @@
JODE (Java Optimize and Decompile Environment) JODE (Java Optimize and Decompile Environment)
This is a decompiler and optimizer for java I have written in my spare JODE is a java package containing a decompiler and an optimizer for
time. The decompiler takes class files as input and produces java. This package is freely available under the GNU General Public
something similar to the original java file. Of course this can't be License.
perfect: There is no way to produce the comments or the names of local
variables (except when the java files were compiled with `-g') and The decompiler reads in class files and produces something similar to
there are often more ways to write the same thing. But jode does its the original java file. Of course this can't be perfect: There is no
job quite well. way to produce the comments or the names of local variables (except
when the java files were compiled with `-g') and there are often more
ways to write the same thing. However, jode does its job quite well.
The optimizer transforms class files in various ways with
can be controlled by a script file.
Please note that most software licenses forbid to decompile class Please note that most software licenses forbid to decompile class
files. Use this decompiler only, if you have legal rights to files. Use this decompiler only, if you have legal rights to
@ -25,6 +30,15 @@ The features of the decompilers are:
Known bugs of the decompiler: Known bugs of the decompiler:
- Some jdk1.3 synthetic access functions aren't understood. The
produced source contains access$xxx functions, but it still compiles.
- There may be other bugs, that cause Exceptions or invalid code.
If you have such a problems don't hesitate to issue a bug report.
Please include the <code>class</code> file if possible.
Limitations:
- If not all dependent classes can be found, the verifier (which is - If not all dependent classes can be found, the verifier (which is
run before decompilation starts) may exit with a type error. You run before decompilation starts) may exit with a type error. You
can decompile it with --verify=off, but take the warning serious, can decompile it with --verify=off, but take the warning serious,
@ -40,18 +54,6 @@ Known bugs of the decompiler:
happen when you compile with `-O' flag and javac has inlined some happen when you compile with `-O' flag and javac has inlined some
methods. methods.
- Sometimes this program may exit with an Exception or produce
incorrect code. If it produced incorrect code the code probably
can't be compiled, so that the error can be easily spotted. If you
have one of these problems (they are very rare now), I would be
very interested in a bug report (including the .class file, if
possible).
- Sometimes it generates some GOTO expression and labels. This
shouldn't happen any more with code produced by javac or jikes.
But some flow obfuscator likes Zelix Klassmaster may provoke this.
In that case you can run the Obfuscator first (to optimize away the
flow obfuscation ;-).
The features of the obfuscator are: The features of the obfuscator are:
* Modular design, you can plug the obfuscation transformation you need * Modular design, you can plug the obfuscation transformation you need
@ -66,62 +68,23 @@ The features of the obfuscator are:
PRELIMINARIES: PRELIMINARIES:
You need java jdk1.2 or above, and the gnu getopt package. See INSTALL for installation instructions.
You should also have the jode-x.x.xx.jar file. Read INSTALL how to
compile it yourself.
Jode also works with jdk1.1, but you need the sun collection classes
and swing in that case.
Here are some URLs for the tools and packages you may need:
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
USAGE: USAGE:
First set the classpath. It should contain the jar file First set the classpath. It should contain the jar file jode-1.1.jar,
jode-x.x.xx.jar, the gnu getopt package and the sun collection package the gnu getopt package and the sun collection package for 1.1. For
for 1.1. For the swingui program you also need swing in you classpath. the swingui program you also need swing in you classpath.
You can then decompile a class file with: You can then decompile a class file with:
java jode.decompiler.Main --classpath jarfile1.jar,jarfile2.jar org.package.Class java jode.decompiler.Main --classpath program.jar,libfoo.jar org.package.Class
or a complete package with
java jode.decompiler.Main --classpath libfoo.jar program.jar
For a graphical user interface based on swing try: For a graphical user interface based on swing try:
java jode.swingui.Main --classpath jarfile1.jar java jode.swingui.Main --classpath jarfile1.jar
The obfuscator/deobfuscator can be run with: The obfuscator/deobfuscator can be run with a script:
java jode.obfuscator.Main obfuscation.jos java jode.obfuscator.Main obfuscation.jos
See XXXX for more information about the script syntax. See the web documents for more information about the script syntax.
HISTORY:
Someday I found guavad, a disassembler for java byte code (it does
similar things like `javap -c'). I used it on a class file, and found
that it was possible to reconstruct the original java code. First I
did it by hand on some small routines, but I soon realized that it was
a rather stupid task, and that I could write a perl script, that does
the same. At the end of the next day I had the first working
decompiler.
Now while it was working, it was not easy to use. You had to
decompile the code first with a disassembler, cut the method, you
wanted to decompile and then run the perl script on it. So I decided
to get some information of the class files and do this all
automatically. I decided to write it in java now, because it suited
best.

@ -12,6 +12,8 @@ Obfuscator:
- Detect Class.forName() calls with constant parameters and rename - Detect Class.forName() calls with constant parameters and rename
these constants. Detect class$ methods with constant parameters. these constants. Detect class$ methods with constant parameters.
Warn about all other occurences of Class.forName() Warn about all other occurences of Class.forName()
- work around Class.forName, by creating a new version using a hash
table that maps md5 sums of old names to obfuscated names.
This should be put into the constant analyzer. The simple This should be put into the constant analyzer. The simple
analyzer should only do the warnings. analyzer should only do the warnings.
@ -55,3 +57,6 @@ User Interface:
Internal: Internal:
- clean up package hierarchy, esp. expr, flow and decompiler. - clean up package hierarchy, esp. expr, flow and decompiler.
- move to net.sf.jode package.
- make the class names more precise, e.g. StructuredBlock is Statement,
FlowBlock is BasicBlock.

@ -1,18 +1,10 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT() AC_INIT()
AM_INIT_AUTOMAKE(jode, 1.0.94) AM_INIT_AUTOMAKE(jode, 1.1)
dnl Checks for programs. dnl Checks for programs.
dnl AC_PROG_CXX
dnl AC_PROG_AWK
dnl AC_PROG_CC
dnl AC_PROG_CPP
dnl AC_PROG_INSTALL
dnl AC_PROG_LN_S
AC_PROG_MAKE_SET AC_PROG_MAKE_SET
dnl AC_PROG_RANLIB
dnl AC_PATH_PROG(ZIP, zip)
AC_PATH_PROG(PERL, perl) AC_PATH_PROG(PERL, perl)
AC_PATH_PROG(CYGPATH, cygpath) AC_PATH_PROG(CYGPATH, cygpath)
@ -190,6 +182,6 @@ test/Makefile,
changequote(, )dnl changequote(, )dnl
if [ \$i != \${i%.java} ]; then if [ \$i != \${i%.java} ]; then
changequote([, ])dnl changequote([, ])dnl
$PERL $srcdir/jcpp $JCPPFLAGS \$i $PERL $srcdir/scripts/jcpp.pl $JCPPFLAGS \$i
fi fi
done]) done])

@ -1,2 +1,3 @@
Makefile Makefile
Makefile.in Makefile.in
*.html

@ -59,6 +59,12 @@ public class ClassAnalyzer
* The minimal visible complexity. * The minimal visible complexity.
*/ */
private static double STEP_COMPLEXITY = 0.03; private static double STEP_COMPLEXITY = 0.03;
/**
* The value of the strictfp modifier.
* JDK1.1 doesn't define it.
*/
private static int STRICTFP = 0x800;
double methodComplexity = 0.0; double methodComplexity = 0.0;
double innerComplexity = 0.0; double innerComplexity = 0.0;
@ -149,6 +155,10 @@ public class ClassAnalyzer
return Modifier.isStatic(modifiers); return Modifier.isStatic(modifiers);
} }
public final boolean isStrictFP() {
return (modifiers & STRICTFP) != 0;
}
public FieldAnalyzer getField(int index) { public FieldAnalyzer getField(int index) {
return fields[index]; return fields[index];
} }
@ -261,6 +271,17 @@ public class ClassAnalyzer
staticConstructor = methods[j]; staticConstructor = methods[j];
else else
constrVector.addElement(methods[j]); constrVector.addElement(methods[j]);
/* Java bytecode can't have strictfp modifier for
* classes, while java can't have strictfp modifier
* for constructors. We handle the difference here.
*
* If only a few constructors are strictfp and the
* methods aren't this would add too much strictfp,
* but that isn't really dangerous.
*/
if (methods[j].isStrictFP())
modifiers |= STRICTFP;
} }
methodComplexity += methods[j].getComplexity(); methodComplexity += methods[j].getComplexity();
} }
@ -435,7 +456,76 @@ public class ClassAnalyzer
public void dumpDeclaration(TabbedPrintWriter writer) throws IOException public void dumpDeclaration(TabbedPrintWriter writer) throws IOException
{ {
dumpSource(writer); dumpDeclaration(writer, null, 0.0, 0.0);
}
public void dumpDeclaration(TabbedPrintWriter writer,
ProgressListener pl, double done, double scale)
throws IOException
{
if (fields == null) {
/* This means that the class could not be loaded.
* give up.
*/
return;
}
writer.startOp(writer.NO_PAREN, 0);
/* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */
int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED
| STRICTFP);
if (clazz.isInterface())
/* interfaces are implicitily abstract */
modifiedModifiers &= ~Modifier.ABSTRACT;
if (parent instanceof MethodAnalyzer) {
/* method scope classes are implicitly private */
modifiedModifiers &= ~Modifier.PRIVATE;
/* anonymous classes are implicitly final */
if (name == null)
modifiedModifiers &= ~Modifier.FINAL;
}
String modif = Modifier.toString(modifiedModifiers);
if (modif.length() > 0)
writer.print(modif + " ");
if (isStrictFP()) {
/* The STRICTFP modifier is set.
* We handle it, since java.lang.reflect.Modifier is too dumb.
*/
writer.print("strictfp ");
}
/* interface is in modif */
if (!clazz.isInterface())
writer.print("class ");
writer.print(name);
ClassInfo superClazz = clazz.getSuperclass();
if (superClazz != null &&
superClazz.getName() != "java.lang.Object") {
writer.breakOp();
writer.print(" extends " + (writer.getClassString
(superClazz, Scope.CLASSNAME)));
}
ClassInfo[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
writer.breakOp();
writer.print(clazz.isInterface() ? " extends " : " implements ");
writer.startOp(writer.EXPL_PAREN, 1);
for (int i=0; i < interfaces.length; i++) {
if (i > 0) {
writer.print(", ");
writer.breakOp();
}
writer.print(writer.getClassString
(interfaces[i], Scope.CLASSNAME));
}
writer.endOp();
}
writer.println();
writer.openBraceClass();
writer.tab();
dumpBlock(writer, pl, done, scale);
writer.untab();
writer.closeBraceClass();
} }
public void dumpBlock(TabbedPrintWriter writer) public void dumpBlock(TabbedPrintWriter writer)
@ -448,6 +538,7 @@ public class ClassAnalyzer
ProgressListener pl, double done, double scale) ProgressListener pl, double done, double scale)
throws IOException throws IOException
{ {
double subScale = scale / getComplexity(); double subScale = scale / getComplexity();
writer.pushScope(this); writer.pushScope(this);
boolean needFieldNewLine = false; boolean needFieldNewLine = false;
@ -541,6 +632,7 @@ public class ClassAnalyzer
needNewLine = true; needNewLine = true;
} }
writer.popScope(); writer.popScope();
clazz.drop(clazz.DECLARATIONS);
} }
public void dumpSource(TabbedPrintWriter writer) public void dumpSource(TabbedPrintWriter writer)
@ -553,64 +645,8 @@ public class ClassAnalyzer
ProgressListener pl, double done, double scale) ProgressListener pl, double done, double scale)
throws IOException throws IOException
{ {
if (fields == null) { dumpDeclaration(writer, pl, done, scale);
/* This means that the class could not be loaded.
* give up.
*/
return;
}
writer.startOp(writer.NO_PAREN, 0);
/* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */
int modifiedModifiers = modifiers & ~Modifier.SYNCHRONIZED;
if (clazz.isInterface())
/* interfaces are implicitily abstract */
modifiedModifiers &= ~Modifier.ABSTRACT;
if (parent instanceof MethodAnalyzer) {
/* method scope classes are implicitly private */
modifiedModifiers &= ~Modifier.PRIVATE;
/* anonymous classes are implicitly final */
if (name == null)
modifiedModifiers &= ~Modifier.FINAL;
}
String modif = Modifier.toString(modifiedModifiers);
if (modif.length() > 0)
writer.print(modif + " ");
/* interface is in modif */
if (!clazz.isInterface())
writer.print("class ");
writer.print(name);
ClassInfo superClazz = clazz.getSuperclass();
if (superClazz != null &&
superClazz.getName() != "java.lang.Object") {
writer.breakOp();
writer.print(" extends " + (writer.getClassString
(superClazz, Scope.CLASSNAME)));
}
ClassInfo[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
writer.breakOp();
writer.print(clazz.isInterface() ? " extends " : " implements ");
writer.startOp(writer.EXPL_PAREN, 1);
for (int i=0; i < interfaces.length; i++) {
if (i > 0) {
writer.print(", ");
writer.breakOp();
}
writer.print(writer.getClassString
(interfaces[i], Scope.CLASSNAME));
}
writer.endOp();
}
writer.println(); writer.println();
writer.openBraceClass();
writer.tab();
dumpBlock(writer, pl, done, scale);
writer.untab();
writer.closeBraceClass();
writer.println();
clazz.drop(clazz.DECLARATIONS);
} }
public void dumpJavaFile(TabbedPrintWriter writer) public void dumpJavaFile(TabbedPrintWriter writer)
@ -648,7 +684,11 @@ public class ClassAnalyzer
} }
public boolean conflicts(String name, int usageType) { public boolean conflicts(String name, int usageType) {
ClassInfo info = clazz; return conflicts(clazz, name, usageType);
}
private static boolean conflicts(ClassInfo info,
String name, int usageType) {
while (info != null) { while (info != null) {
if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) { if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) {
MethodInfo[] minfos = info.getMethods(); MethodInfo[] minfos = info.getMethods();
@ -676,6 +716,11 @@ public class ClassAnalyzer
if (usageType == NOSUPERFIELDNAME if (usageType == NOSUPERFIELDNAME
|| usageType == NOSUPERMETHODNAME) || usageType == NOSUPERMETHODNAME)
return false; return false;
ClassInfo[] ifaces = info.getInterfaces();
for (int i = 0; i < ifaces.length; i++)
if (conflicts(ifaces[i], name, usageType))
return true;
info = info.getSuperclass(); info = info.getSuperclass();
} }
return false; return false;

@ -221,11 +221,9 @@ public class Decompiler {
classPath = new ClassPath(cp); classPath = new ClassPath(cp);
} }
/* XXX, comment the next line, as soon as ClassInfo.forName is
* no longer used. */
ClassInfo.setClassPath(classPath);
ClassInfo clazz = classPath.getClassInfo(className); ClassInfo clazz = classPath.getClassInfo(className);
ImportHandler imports = new ImportHandler(importPackageLimit, ImportHandler imports = new ImportHandler(classPath,
importPackageLimit,
importClassLimit); importClassLimit);
TabbedPrintWriter tabbedWriter = TabbedPrintWriter tabbedWriter =
new TabbedPrintWriter(writer, imports, false, new TabbedPrintWriter(writer, imports, false,

@ -59,7 +59,7 @@ public class FieldAnalyzer implements Analyzer {
if (fd.getConstant() != null) { if (fd.getConstant() != null) {
constant = new ConstOperator(fd.getConstant()); constant = new ConstOperator(fd.getConstant());
constant.setType(type); constant.setType(type);
constant.makeInitializer(); constant.makeInitializer(type);
} }
} }
@ -109,7 +109,7 @@ public class FieldAnalyzer implements Analyzer {
} }
analyzedSynthetic(); analyzedSynthetic();
} else } else
expr.makeInitializer(); expr.makeInitializer(type);
constant = expr; constant = expr;
return true; return true;

@ -20,6 +20,7 @@
package jode.decompiler; package jode.decompiler;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath;
import jode.type.Type; import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.type.ClassInfoType; import jode.type.ClassInfoType;
@ -53,6 +54,7 @@ public class ImportHandler {
/* Classes that doesn't need to be qualified. */ /* Classes that doesn't need to be qualified. */
Hashtable cachedClassNames = null; Hashtable cachedClassNames = null;
ClassAnalyzer main; ClassAnalyzer main;
ClassPath classPath;
String className; String className;
String pkg; String pkg;
@ -77,11 +79,13 @@ public class ImportHandler {
} }
}; };
public ImportHandler() { public ImportHandler(ClassPath classPath) {
this(DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT); this(classPath, DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT);
} }
public ImportHandler(int packageLimit, int classLimit) { public ImportHandler(ClassPath classPath,
int packageLimit, int classLimit) {
this.classPath = classPath;
importPackageLimit = packageLimit; importPackageLimit = packageLimit;
importClassLimit = classLimit; importClassLimit = classLimit;
} }
@ -112,12 +116,12 @@ public class ImportHandler {
if (pkg.length() != 0) { if (pkg.length() != 0) {
/* Does this conflict with a class in this package? */ /* Does this conflict with a class in this package? */
if (ClassInfo.exists(pkg+name)) if (classPath.existsClass(pkg+name))
return true; return true;
} else { } else {
/* Does this conflict with a class in this unnamed /* Does this conflict with a class in this unnamed
* package? */ * package? */
if (ClassInfo.exists(name.substring(1))) if (classPath.existsClass(name.substring(1)))
return true; return true;
} }
@ -129,7 +133,7 @@ public class ImportHandler {
importName = importName.substring importName = importName.substring
(0, importName.length()-2); (0, importName.length()-2);
if (!importName.equals(pkgName)) { if (!importName.equals(pkgName)) {
if (ClassInfo.exists(importName+name)) if (classPath.existsClass(importName+name))
return true; return true;
} }
} else { } else {
@ -399,5 +403,3 @@ public class ImportHandler {
return 1; return 1;
} }
} }

@ -388,8 +388,7 @@ public class LocalInfo implements Declarable {
if (((LocalVarOperator) enum.nextElement()).isWrite()) if (((LocalVarOperator) enum.nextElement()).isWrite())
writes++; writes++;
} }
if (writes > 1) /* FIXME: Check if declaring final is okay */
return false;
li.isFinal = true; li.isFinal = true;
return true; return true;
} }

@ -171,7 +171,7 @@ public class Main extends Options {
("Can't read "+ex.getMessage()+"."); ("Can't read "+ex.getMessage()+".");
GlobalOptions.err.println GlobalOptions.err.println
("Check the class path ("+classPathStr+ ("Check the class path ("+classPathStr+
") and check that you use the java class name."); ") and check that you use the java class name.");
} catch (IOException ex) { } catch (IOException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("Can't write source of "+className+"."); ("Can't write source of "+className+".");
@ -181,6 +181,22 @@ public class Main extends Options {
} }
public static void main(String[] params) { public static void main(String[] params) {
try {
decompile(params);
} catch (ExceptionInInitializerError ex) {
ex.getException().printStackTrace();
} catch (Throwable ex) {
ex.printStackTrace();
}
/* When AWT applications are compiled with insufficient
* classpath the type guessing by reflection code can
* generate an awt thread that will prevent normal
* exiting.
*/
System.exit(0);
}
public static void decompile(String[] params) {
if (params.length == 0) { if (params.length == 0) {
usage(); usage();
return; return;
@ -305,8 +321,8 @@ public class Main extends Options {
if (errorInParams) if (errorInParams)
return; return;
classPath = new ClassPath(classPathStr); classPath = new ClassPath(classPathStr);
ClassInfo.setClassPath(classPath); ImportHandler imports = new ImportHandler(classPath,
ImportHandler imports = new ImportHandler(importPackageLimit, importPackageLimit,
importClassLimit); importClassLimit);
ZipOutputStream destZip = null; ZipOutputStream destZip = null;

@ -83,6 +83,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* The minimal visible complexity. * The minimal visible complexity.
*/ */
private static double STEP_COMPLEXITY = 0.01; private static double STEP_COMPLEXITY = 0.01;
/**
* The value of the strictfp modifier.
* JDK1.1 doesn't define it.
*/
private static int STRICTFP = 0x800;
/** /**
* The import handler where we should register our types. * The import handler where we should register our types.
*/ */
@ -310,6 +315,14 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
return minfo.isSynthetic(); return minfo.isSynthetic();
} }
/**
* Checks if this method is strictfp
* @return true, iff this method is synthetic.
*/
public final boolean isStrictFP() {
return (minfo.getModifiers() & STRICTFP) != 0;
}
/** /**
* Tells if this method is the constructor$xx method generated by jikes. * Tells if this method is the constructor$xx method generated by jikes.
* @param value true, iff this method is the jikes constructor. * @param value true, iff this method is the jikes constructor.
@ -681,7 +694,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (synth.getKind() == synth.GETCLASS) if (synth.getKind() == synth.GETCLASS)
return true; return true;
if (synth.getKind() >= synth.ACCESSGETFIELD if (synth.getKind() >= synth.ACCESSGETFIELD
&& synth.getKind() <= synth.ACCESSCONSTRUCTOR && synth.getKind() <= synth.ACCESSDUPPUTSTATIC
&& (Options.options & Options.OPTION_INNER) != 0 && (Options.options & Options.OPTION_INNER) != 0
&& (Options.options & Options.OPTION_ANON) != 0) && (Options.options & Options.OPTION_ANON) != 0)
return true; return true;
@ -711,10 +724,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (isJikesBlockInitializer) if (isJikesBlockInitializer)
return true; return true;
/* The default constructor must be empty of course */ /* The default constructor must be empty
* and mustn't throw exceptions */
if (getMethodHeader() == null if (getMethodHeader() == null
|| !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock) || !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock)
|| !getMethodHeader().hasNoJumps()) || !getMethodHeader().hasNoJumps()
|| exceptions.length > 0)
return false; return false;
if (declareAsConstructor if (declareAsConstructor
@ -804,7 +819,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* as final. * as final.
*/ */
if (isConstructor() && isStatic()) if (isConstructor() && isStatic())
modifiedModifiers &= ~Modifier.FINAL; modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC
| Modifier.PROTECTED | Modifier.PRIVATE);
modifiedModifiers &= ~STRICTFP;
writer.startOp(writer.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 0);
writer.startOp(writer.NO_PAREN, 5); writer.startOp(writer.NO_PAREN, 5);
@ -819,6 +836,22 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.print(delim + modif); writer.print(delim + modif);
if (modif.length() > 0) if (modif.length() > 0)
delim = " "; delim = " ";
if (isStrictFP()) {
/* The STRICTFP modifier is set.
* We handle it, since java.lang.reflect.Modifier is too dumb.
*/
/* If STRICTFP is already set for class don't set it for method.
* And don't set STRICTFP for native methods or constructors.
*/
if (!classAnalyzer.isStrictFP()
&& !isConstructor()
&& (modifiedModifiers & Modifier.NATIVE) == 0) {
writer.print(delim + "strictfp");
delim = " ";
}
}
if (isConstructor if (isConstructor
&& (isStatic() && (isStatic()
|| (classAnalyzer.getName() == null || (classAnalyzer.getName() == null

@ -1,4 +1,4 @@
/* OuterValues Copyright (C) 1998-1999 Jochen Hoenicke. /* OuterValues Copyright (C) 1998-2001 Jochen Hoenicke.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -94,6 +94,7 @@ public class OuterValues
private Expression[] head; private Expression[] head;
private Vector ovListeners; private Vector ovListeners;
private boolean jikesAnonymousInner; private boolean jikesAnonymousInner;
private boolean implicitOuterClass;
/** /**
* The maximal number of parameters used for outer values. * The maximal number of parameters used for outer values.
@ -293,6 +294,17 @@ public class OuterValues
return jikesAnonymousInner; return jikesAnonymousInner;
} }
/**
* Javac 1.3 doesn't give an outer class reference for anonymous
* classes that extend inner classes, provided the outer class is
* the normal this parameter. Instead it takes a normal outer
* value parameter for this. This method tells if this is such a
* class.
*/
public boolean isImplicitOuterClass() {
return implicitOuterClass;
}
public void addOuterValueListener(OuterValueListener l) { public void addOuterValueListener(OuterValueListener l) {
if (ovListeners == null) if (ovListeners == null)
ovListeners = new Vector(); ovListeners = new Vector();
@ -308,6 +320,10 @@ public class OuterValues
jikesAnonymousInner = value; jikesAnonymousInner = value;
} }
public void setImplicitOuterClass(boolean value) {
implicitOuterClass = value;
}
private static int countSlots(Expression[] exprs, int length) { private static int countSlots(Expression[] exprs, int length) {
int slots = 0; int slots = 0;
for (int i=0; i < length; i++) for (int i=0; i < length; i++)
@ -369,6 +385,8 @@ public class OuterValues
} }
if (jikesAnonymousInner) if (jikesAnonymousInner)
sb.append("!jikesAnonymousInner"); sb.append("!jikesAnonymousInner");
if (implicitOuterClass)
sb.append("!implicitOuterClass");
return sb.append("]").toString(); return sb.append("]").toString();
} }
} }

@ -1,4 +1,4 @@
/* TabbedPrintWriter Copyright (C) 1998-1999 Jochen Hoenicke. /* TabbedPrintWriter Copyright (C) 1998-2001 Jochen Hoenicke.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -111,11 +111,8 @@ public class TabbedPrintWriter {
} }
public void startOp(int opts, int penalty, int pos) { public void startOp(int opts, int penalty, int pos) {
if (startPos != -1) { if (startPos != -1)
GlobalOptions.err.println("WARNING: missing breakOp"); throw new InternalError("missing breakOp");
Thread.dumpStack();
return;
}
startPos = pos; startPos = pos;
options = opts; options = opts;
breakPenalty = penalty; breakPenalty = penalty;
@ -567,8 +564,9 @@ public class TabbedPrintWriter {
Stack state = new Stack(); Stack state = new Stack();
int pos = currentLine.length(); int pos = currentLine.length();
while (currentBP.parentBP != null) { while (currentBP.parentBP != null) {
state.push(new Integer(currentBP.options));
state.push(new Integer(currentBP.breakPenalty)); state.push(new Integer(currentBP.breakPenalty));
/* We don't want parentheses or unconventional line breaking */
currentBP.options = DONT_BREAK;
currentBP.endPos = pos; currentBP.endPos = pos;
currentBP = currentBP.parentBP; currentBP = currentBP.parentBP;
} }
@ -579,8 +577,7 @@ public class TabbedPrintWriter {
Stack state = (Stack) s; Stack state = (Stack) s;
while (!state.isEmpty()) { while (!state.isEmpty()) {
int penalty = ((Integer) state.pop()).intValue(); int penalty = ((Integer) state.pop()).intValue();
int options = ((Integer) state.pop()).intValue(); startOp(DONT_BREAK, penalty);
startOp(options, penalty);
} }
} }

@ -32,4 +32,29 @@ public class ArrayStoreOperator extends ArrayLoadOperator
public boolean matches(Operator loadop) { public boolean matches(Operator loadop) {
return loadop instanceof ArrayLoadOperator; return loadop instanceof ArrayLoadOperator;
} }
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
Type arrType = subExpressions[0].getType().getHint();
if (arrType instanceof ArrayType) {
Type elemType = ((ArrayType) arrType).getElementType();
if (!elemType.isOfType(getType())) {
/* We need an explicit widening cast */
writer.print("(");
writer.startOp(writer.EXPL_PAREN, 1);
writer.print("(");
writer.printType(Type.tArray(getType().getHint()));
writer.print(") ");
writer.breakOp();
subExpressions[0].dumpExpression(writer, 700);
writer.print(")");
writer.breakOp();
writer.print("[");
subExpressions[1].dumpExpression(writer, 0);
writer.print("]");
return;
}
}
super.dumpExpression(writer);
}
} }

@ -110,7 +110,7 @@ public class ConstOperator extends NoArgOperator {
return false; return false;
} }
public void makeInitializer() { public void makeInitializer(Type type) {
isInitializer = true; isInitializer = true;
} }

@ -48,7 +48,7 @@ public class ConstantArrayOperator extends Operator {
empty = new ConstOperator(emptyVal); empty = new ConstOperator(emptyVal);
empty.setType(argType); empty.setType(argType);
empty.makeInitializer(); empty.makeInitializer(argType);
initOperands(size); initOperands(size);
for (int i=0; i < subExpressions.length; i++) for (int i=0; i < subExpressions.length; i++)
setSubExpressions(i, empty); setSubExpressions(i, empty);
@ -74,7 +74,7 @@ public class ConstantArrayOperator extends Operator {
setType(Type.tSuperType(Type.tArray(value.getType()))); setType(Type.tSuperType(Type.tArray(value.getType())));
subExpressions[index] = value; subExpressions[index] = value;
value.parent = this; value.parent = this;
value.makeInitializer(); value.makeInitializer(argType);
return true; return true;
} }
@ -82,8 +82,9 @@ public class ConstantArrayOperator extends Operator {
return 200; return 200;
} }
public void makeInitializer() { public void makeInitializer(Type type) {
isInitializer = true; if (type.getHint().isOfType(getType()))
isInitializer = true;
} }
public Expression simplify() { public Expression simplify() {

@ -210,7 +210,7 @@ public abstract class Expression {
return null; return null;
} }
public void makeInitializer() { public void makeInitializer(Type type) {
} }
public boolean isConstant() { public boolean isConstant() {

@ -26,6 +26,7 @@ import jode.bytecode.FieldInfo;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath; import jode.bytecode.ClassPath;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.bytecode.TypeSignature;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
import jode.decompiler.ClassAnalyzer; import jode.decompiler.ClassAnalyzer;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
@ -35,6 +36,7 @@ import jode.decompiler.TabbedPrintWriter;
import jode.decompiler.Scope; import jode.decompiler.Scope;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Modifier;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.Collection; import java.util.Collection;
///#enddef ///#enddef
@ -159,6 +161,34 @@ public abstract class FieldOperator extends Operator {
return clazz.getFields(); return clazz.getFields();
} }
private static FieldInfo getFieldInfo(ClassInfo clazz,
String name, String type) {
while (clazz != null) {
FieldInfo field = clazz.findField(name, type);
if (field != null)
return field;
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i = 0; i < ifaces.length; i++) {
field = getFieldInfo(ifaces[i], name, type);
if (field != null)
return field;
}
clazz = clazz.getSuperclass();
}
return null;
}
public FieldInfo getFieldInfo() {
ClassInfo clazz;
if (ref.getClazz().charAt(0) == '[')
clazz = classPath.getClassInfo("java.lang.Object");
else
clazz = TypeSignature.getClassInfo(classPath, ref.getClazz());
return getFieldInfo(clazz, ref.getName(), ref.getType());
}
public boolean needsCast(Type type) { public boolean needsCast(Type type) {
if (type instanceof NullType) if (type instanceof NullType)
return true; return true;
@ -168,6 +198,37 @@ public abstract class FieldOperator extends Operator {
ClassInfo clazz = ((ClassInfoType) classType).getClassInfo(); ClassInfo clazz = ((ClassInfoType) classType).getClassInfo();
ClassInfo parClazz = ((ClassInfoType) type).getClassInfo(); ClassInfo parClazz = ((ClassInfoType) type).getClassInfo();
FieldInfo field = clazz.findField(ref.getName(), ref.getType());
find_field:
while (field == null) {
ClassInfo ifaces[] = clazz.getInterfaces();
for (int i = 0; i < ifaces.length; i++) {
field = ifaces[i].findField(ref.getName(), ref.getType());
if (field != null)
break find_field;
}
clazz = clazz.getSuperclass();
if (clazz == null)
/* Weird, field not existing? */
return false;
field = clazz.findField(ref.getName(), ref.getType());
}
if (Modifier.isPrivate(field.getModifiers()))
return parClazz != clazz;
else if ((field.getModifiers()
& (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
/* Field is protected. We need a cast if parClazz is in
* other package than clazz.
*/
int lastDot = clazz.getName().lastIndexOf('.');
if (lastDot == -1
|| lastDot != parClazz.getName().lastIndexOf('.')
|| !(parClazz.getName()
.startsWith(clazz.getName().substring(0,lastDot))))
return true;
}
while (clazz != parClazz && clazz != null) { while (clazz != parClazz && clazz != null) {
FieldInfo[] fields = parClazz.getFields(); FieldInfo[] fields = parClazz.getFields();
for (int i = 0; i < fields.length; i++) { for (int i = 0; i < fields.length; i++) {
@ -246,20 +307,8 @@ public abstract class FieldOperator extends Operator {
*/ */
getField() == null getField() == null
&& writer.conflicts(fieldName, null, && writer.conflicts(fieldName, null,
Scope.NOSUPERFIELDNAME))) { Scope.NOSUPERFIELDNAME))) {
thisOp.dumpExpression(writer, 950);
ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
while (ana.getParent() instanceof ClassAnalyzer
&& ana != scope)
ana = (ClassAnalyzer) ana.getParent();
if (ana == scope)
// For a simple outer class we can say this
writer.print("this");
else {
// For a class that owns a method that owns
// us, we have to give the full class name
thisOp.dumpExpression(writer, 950);
}
writer.breakOp(); writer.breakOp();
writer.print("."); writer.print(".");
} }

@ -62,6 +62,7 @@ public final class InvokeOperator extends Operator
int methodFlag; int methodFlag;
MethodType methodType; MethodType methodType;
String methodName; String methodName;
Reference ref;
int skippedArgs; int skippedArgs;
ClassType classType; ClassType classType;
Type[] hints; Type[] hints;
@ -149,6 +150,7 @@ public final class InvokeOperator extends Operator
int methodFlag, Reference reference) { int methodFlag, Reference reference) {
super(Type.tUnknown, 0); super(Type.tUnknown, 0);
this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath(); this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath();
this.ref = reference;
this.methodType = Type.tMethod(classPath, reference.getType()); this.methodType = Type.tMethod(classPath, reference.getType());
this.methodName = reference.getName(); this.methodName = reference.getName();
this.classType = (ClassType) this.classType = (ClassType)
@ -208,6 +210,26 @@ public final class InvokeOperator extends Operator
return methodName; return methodName;
} }
private static MethodInfo getMethodInfo(ClassInfo clazz,
String name, String type) {
while (clazz != null) {
MethodInfo method = clazz.findMethod(name, type);
if (method != null)
return method;
clazz = clazz.getSuperclass();
}
return null;
}
public MethodInfo getMethodInfo() {
ClassInfo clazz;
if (ref.getClazz().charAt(0) == '[')
clazz = classPath.getClassInfo("java.lang.Object");
else
clazz = TypeSignature.getClassInfo(classPath, ref.getClazz());
return getMethodInfo(clazz, ref.getName(), ref.getType());
}
public Type getClassType() { public Type getClassType() {
return classType; return classType;
} }
@ -603,14 +625,20 @@ public final class InvokeOperator extends Operator
synth.getReference()); synth.getReference());
break; break;
case SyntheticAnalyzer.ACCESSPUTFIELD: case SyntheticAnalyzer.ACCESSPUTFIELD:
case SyntheticAnalyzer.ACCESSDUPPUTFIELD:
op = new StoreInstruction op = new StoreInstruction
(new PutFieldOperator(methodAnalyzer, false, (new PutFieldOperator(methodAnalyzer, false,
synth.getReference())); synth.getReference()));
if (synth.getKind() == synth.ACCESSDUPPUTFIELD)
((StoreInstruction) op).makeNonVoid();
break; break;
case SyntheticAnalyzer.ACCESSPUTSTATIC: case SyntheticAnalyzer.ACCESSPUTSTATIC:
case SyntheticAnalyzer.ACCESSDUPPUTSTATIC:
op = new StoreInstruction op = new StoreInstruction
(new PutFieldOperator(methodAnalyzer, true, (new PutFieldOperator(methodAnalyzer, true,
synth.getReference())); synth.getReference()));
if (synth.getKind() == synth.ACCESSDUPPUTSTATIC)
((StoreInstruction) op).makeNonVoid();
break; break;
case SyntheticAnalyzer.ACCESSMETHOD: case SyntheticAnalyzer.ACCESSMETHOD:
op = new InvokeOperator(methodAnalyzer, ACCESSSPECIAL, op = new InvokeOperator(methodAnalyzer, ACCESSSPECIAL,
@ -669,9 +697,35 @@ public final class InvokeOperator extends Operator
Type realClassType; Type realClassType;
if (methodFlag == STATIC) if (methodFlag == STATIC)
realClassType = classType; realClassType = classType;
else { else if (param == 0) {
if (param == 0) if (paramTypes[0] instanceof NullType)
return paramTypes[0] instanceof NullType; return true;
if (!(paramTypes[0] instanceof ClassInfoType
&& classType instanceof ClassInfoType))
return false;
ClassInfo clazz = ((ClassInfoType) classType).getClassInfo();
ClassInfo parClazz
= ((ClassInfoType) paramTypes[0]).getClassInfo();
MethodInfo method = getMethodInfo();
if (method == null)
/* This is a NoSuchMethodError */
return false;
if (Modifier.isPrivate(method.getModifiers()))
return parClazz != clazz;
else if ((method.getModifiers()
& (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
/* Method is protected. We need a cast if parClazz is in
* other package than clazz.
*/
int lastDot = clazz.getName().lastIndexOf('.');
if (lastDot != parClazz.getName().lastIndexOf('.')
|| !(parClazz.getName()
.startsWith(clazz.getName().substring(0,lastDot+1))))
return true;
}
return false;
} else {
realClassType = paramTypes[0]; realClassType = paramTypes[0];
} }
@ -765,6 +819,7 @@ public final class InvokeOperator extends Operator
int arg = 1; int arg = 1;
int length = subExpressions.length; int length = subExpressions.length;
boolean jikesAnonymousInner = false; boolean jikesAnonymousInner = false;
boolean implicitOuterClass = false;
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
&& clazzAna != null && clazz.isMethodScoped()) { && clazzAna != null && clazz.isMethodScoped()) {
@ -772,6 +827,7 @@ public final class InvokeOperator extends Operator
OuterValues ov = clazzAna.getOuterValues(); OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount(); arg += ov.getCount();
jikesAnonymousInner = ov.isJikesAnonymousInner(); jikesAnonymousInner = ov.isJikesAnonymousInner();
implicitOuterClass = ov.isImplicitOuterClass();
for (int i=1; i< arg; i++) { for (int i=1; i< arg; i++) {
Expression expr = subExpressions[i]; Expression expr = subExpressions[i];
@ -799,7 +855,9 @@ public final class InvokeOperator extends Operator
if ((~Options.options & (Options.OPTION_INNER if ((~Options.options & (Options.OPTION_INNER
| Options.OPTION_CONTRAFO)) == 0 | Options.OPTION_CONTRAFO)) == 0
&& clazz.getOuterClass() != null && clazz.getOuterClass() != null
&& !Modifier.isStatic(clazz.getModifiers())) { && !Modifier.isStatic(clazz.getModifiers())
&& !implicitOuterClass
&& arg < length) {
Expression outerExpr = jikesAnonymousInner Expression outerExpr = jikesAnonymousInner
? subExpressions[--length] ? subExpressions[--length]
@ -883,6 +941,7 @@ public final class InvokeOperator extends Operator
boolean qualifiedNew = false; boolean qualifiedNew = false;
boolean jikesAnonymousInner = false; boolean jikesAnonymousInner = false;
boolean implicitOuterClass = false;
/* clazz != null, since an array doesn't have a constructor */ /* clazz != null, since an array doesn't have a constructor */
@ -894,6 +953,7 @@ public final class InvokeOperator extends Operator
OuterValues ov = clazzAna.getOuterValues(); OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount(); arg += ov.getCount();
jikesAnonymousInner = ov.isJikesAnonymousInner(); jikesAnonymousInner = ov.isJikesAnonymousInner();
implicitOuterClass = ov.isImplicitOuterClass();
if (clazz.getClassName() == null) { if (clazz.getClassName() == null) {
/* This is an anonymous class */ /* This is an anonymous class */
@ -933,47 +993,54 @@ public final class InvokeOperator extends Operator
(Options.OPTION_INNER (Options.OPTION_INNER
| Options.OPTION_CONTRAFO)) == 0) { | Options.OPTION_CONTRAFO)) == 0) {
Expression outerExpr = jikesAnonymousInner if (implicitOuterClass) {
? subExpressions[--length] /* Outer class is "this" and is not given
: subExpressions[arg++]; * explicitly. No need to print something.
if (outerExpr instanceof CheckNullOperator) { */
CheckNullOperator cno = (CheckNullOperator) outerExpr; } else if (arg < length) {
outerExpr = cno.subExpressions[0]; Expression outerExpr = jikesAnonymousInner
} else if (!(outerExpr instanceof ThisOperator)) { ? subExpressions[--length]
// Complain about missing checknull, but not if : subExpressions[arg++];
// that is the known bug in jikes. if (outerExpr instanceof CheckNullOperator) {
if (!jikesAnonymousInner) CheckNullOperator cno = (CheckNullOperator) outerExpr;
writer.print("MISSING CHECKNULL "); outerExpr = cno.subExpressions[0];
} } else {
/* We used to complain about MISSING CHECKNULL
* here except for ThisOperators. But javac
* v8 doesn't seem to create CHECKNULL ops.
*/
}
if (outerExpr instanceof ThisOperator) { if (outerExpr instanceof ThisOperator) {
Scope scope = writer.getScope Scope scope = writer.getScope
(((ThisOperator) outerExpr).getClassInfo(), (((ThisOperator) outerExpr).getClassInfo(),
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (writer.conflicts(clazz.getClassName(), if (writer.conflicts(clazz.getClassName(),
scope, Scope.CLASSNAME)) { scope, Scope.CLASSNAME)) {
qualifiedNew = true;
outerExpr.dumpExpression(writer, 950);
writer.breakOp();
writer.print(".");
}
} else {
qualifiedNew = true; qualifiedNew = true;
outerExpr.dumpExpression(writer, 950); if (outerExpr.getType() instanceof NullType) {
writer.print("(");
writer.startOp(writer.EXPL_PAREN, 1);
writer.print("(");
writer.printType(Type.tClass(clazz));
writer.print(") ");
writer.breakOp();
outerExpr.dumpExpression(writer, 700);
writer.endOp();
writer.print(")");
} else
outerExpr.dumpExpression(writer, 950);
writer.breakOp(); writer.breakOp();
writer.print("."); writer.print(".");
} }
} else { } else
qualifiedNew = true; writer.print("MISSING OUTEREXPR ");
if (outerExpr.getType() instanceof NullType) {
writer.print("(");
writer.startOp(writer.EXPL_PAREN, 1);
writer.print("(");
writer.printType(Type.tClass(clazz));
writer.print(") ");
writer.breakOp();
outerExpr.dumpExpression(writer, 700);
writer.endOp();
writer.print(")");
} else
outerExpr.dumpExpression(writer, 950);
writer.breakOp();
writer.print(".");
}
} }
if (subExpressions[0] instanceof NewOperator if (subExpressions[0] instanceof NewOperator
@ -1102,10 +1169,10 @@ public final class InvokeOperator extends Operator
Scope scope = writer.getScope(thisOp.getClassInfo(), Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (writer.conflicts(methodName, scope, Scope.METHODNAME) if (writer.conflicts(methodName, scope, Scope.METHODNAME)
|| (/* This field is inherited from the parent of || (/* This method is inherited from the parent of
* an outer class, or it is inherited from the * an outer class, or it is inherited from the
* parent of this class and there is a conflicting * parent of this class and there is a conflicting
* field in some outer class. * method in some outer class.
*/ */
getMethodAnalyzer() == null getMethodAnalyzer() == null
&& (!isThis() || && (!isThis() ||

@ -19,6 +19,7 @@
package jode.flow; package jode.flow;
import jode.expr.ConstOperator; import jode.expr.ConstOperator;
import jode.type.Type;
/** /**
* This block represents a case instruction. A case instruction is a * This block represents a case instruction. A case instruction is a
@ -171,8 +172,9 @@ public class CaseBlock extends StructuredBlock {
writer.untab(); writer.untab();
} }
ConstOperator constOp = new ConstOperator(new Integer(value)); ConstOperator constOp = new ConstOperator(new Integer(value));
constOp.setType(((SwitchBlock)outer).getInstruction().getType()); Type type = ((SwitchBlock)outer).getInstruction().getType();
constOp.makeInitializer(); constOp.setType(type);
constOp.makeInitializer(type);
writer.print("case " + constOp.toString() + ":"); writer.print("case " + constOp.toString() + ":");
} }
if (subBlock instanceof EmptyBlock if (subBlock instanceof EmptyBlock

@ -114,7 +114,6 @@ public class InstructionBlock extends InstructionContainer {
* change this to a initializing variable declaration. * change this to a initializing variable declaration.
*/ */
isDeclaration = true; isDeclaration = true;
storeOp.getSubExpressions()[1].makeInitializer();
declareSet.remove(local); declareSet.remove(local);
} }
} }
@ -142,6 +141,7 @@ public class InstructionBlock extends InstructionContainer {
local.dumpDeclaration(writer); local.dumpDeclaration(writer);
writer.breakOp(); writer.breakOp();
writer.print(" = "); writer.print(" = ");
store.getSubExpressions()[1].makeInitializer(local.getType());
store.getSubExpressions()[1].dumpExpression(writer.IMPL_PAREN, store.getSubExpressions()[1].dumpExpression(writer.IMPL_PAREN,
writer); writer);
writer.endOp(); writer.endOp();

@ -33,12 +33,21 @@ public class JsrBlock extends StructuredBlock {
* The inner block that jumps to the subroutine. * The inner block that jumps to the subroutine.
*/ */
StructuredBlock innerBlock; StructuredBlock innerBlock;
boolean good = false;
public JsrBlock() { public JsrBlock() {
innerBlock = new EmptyBlock(); innerBlock = new EmptyBlock();
innerBlock.outer = this; innerBlock.outer = this;
} }
public void setGood(boolean g) {
good = g;
}
public boolean isGood() {
return good;
}
/** /**
* Sets the successors of this structured block. This should be only * Sets the successors of this structured block. This should be only
* called once, by FlowBlock.setSuccessors(). * called once, by FlowBlock.setSuccessors().

@ -269,7 +269,6 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
* change this to a initializing variable declaration. * change this to a initializing variable declaration.
*/ */
isDeclaration = true; isDeclaration = true;
storeOp.getSubExpressions()[1].makeInitializer();
declareSet.remove(local); declareSet.remove(local);
} }
} }
@ -336,6 +335,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
local.dumpDeclaration(writer); local.dumpDeclaration(writer);
writer.breakOp(); writer.breakOp();
writer.print(" = "); writer.print(" = ");
store.getSubExpressions()[1]
.makeInitializer(local.getType());
store.getSubExpressions()[1].dumpExpression(writer, 100); store.getSubExpressions()[1].dumpExpression(writer, 100);
writer.endOp(); writer.endOp();
} else } else

@ -39,6 +39,7 @@ MY_JAVA_FILES = \
RetBlock.java \ RetBlock.java \
ReturnBlock.java \ ReturnBlock.java \
SequentialBlock.java \ SequentialBlock.java \
SlotSet.java \
SpecialBlock.java \ SpecialBlock.java \
StructuredBlock.java \ StructuredBlock.java \
SwitchBlock.java \ SwitchBlock.java \

@ -147,6 +147,9 @@ public class SpecialBlock extends StructuredBlock {
* to: * to:
* method_invocation() * method_invocation()
* *
* With java1.3 due to access$ methods the method_invocation can
* already be a non void store instruction.
*
* PUSH arg1 * PUSH arg1
* PUSH arg2 * PUSH arg2
* POP2 * POP2
@ -173,7 +176,8 @@ public class SpecialBlock extends StructuredBlock {
if (instr.getType().stackSize() == count) { if (instr.getType().stackSize() == count) {
StructuredBlock newBlock; StructuredBlock newBlock;
if (instr instanceof InvokeOperator) { if (instr instanceof InvokeOperator
|| instr instanceof StoreInstruction) {
Expression newExpr Expression newExpr
= new PopOperator(instr.getType()).addOperand(instr); = new PopOperator(instr.getType()).addOperand(instr);
prev.setInstruction(newExpr); prev.setInstruction(newExpr);

@ -32,6 +32,8 @@ import jode.expr.*;
import jode.type.MethodType; import jode.type.MethodType;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath;
import jode.bytecode.MethodInfo;
import java.io.IOException; import java.io.IOException;
import java.util.Vector; import java.util.Vector;
@ -325,6 +327,17 @@ public class TransformConstructors {
} }
} }
if (minSuperOuter == 1
&& superAna.getParent() instanceof ClassAnalyzer) {
/* Check if this is the implicit Outer Class */
LocalLoadOperator llop = (LocalLoadOperator) subExpr[start];
if (outerValues.getValueBySlot(llop.getLocalInfo().getSlot())
instanceof ThisOperator) {
minSuperOuter = 0;
outerValues.setImplicitOuterClass(true);
}
}
if (minSuperOuter > 0) { if (minSuperOuter > 0) {
if (superOV == null || superOV.getCount() < minSuperOuter) { if (superOV == null || superOV.getCount() < minSuperOuter) {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
@ -616,6 +629,29 @@ public class TransformConstructors {
fo.getFieldType()) >= fieldSlot) fo.getFieldType()) >= fieldSlot)
return null; return null;
} }
if (expr instanceof InvokeOperator) {
/* Don't allow method invocations that can throw a checked
* exception to leave the constructor.
*/
MethodInfo method = ((InvokeOperator) expr).getMethodInfo();
String[] excs = method == null ? null : method.getExceptions();
if (excs != null) {
ClassPath classPath = clazzAnalyzer.getClassPath();
ClassInfo runtimeException
= classPath.getClassInfo("java.lang.RuntimeException");
ClassInfo error = classPath.getClassInfo("java.lang.Error");
for (int i = 0; i < excs.length; i++) {
ClassInfo exClass = classPath.getClassInfo(excs[i]);
try {
if (!runtimeException.superClassOf(exClass)
&& !error.superClassOf(exClass))
return null;
} catch (IOException ex) {
return null;
}
}
}
}
if (expr instanceof Operator) { if (expr instanceof Operator) {
Operator op = (Operator) expr; Operator op = (Operator) expr;
Expression[] subExpr = op.getSubExpressions(); Expression[] subExpr = op.getSubExpressions();

@ -1,4 +1,4 @@
/* TransformExceptionHandlers Copyright (C) 1998-1999 Jochen Hoenicke. /* TransformExceptionHandlers Copyright (C) 1998-2001 Jochen Hoenicke.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -210,7 +210,7 @@ public class TransformExceptionHandlers {
* @param tryFlow the FlowBLock of the try block. * @param tryFlow the FlowBLock of the try block.
* @param subRoutine the FlowBlock of the sub routine. * @param subRoutine the FlowBlock of the sub routine.
*/ */
private void removeBadJSR(FlowBlock tryFlow, StructuredBlock catchBlock, private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock,
FlowBlock subRoutine) { FlowBlock subRoutine) {
Jump nextJump; Jump nextJump;
for (Jump jumps = tryFlow.getJumps(subRoutine); for (Jump jumps = tryFlow.getJumps(subRoutine);
@ -220,18 +220,27 @@ public class TransformExceptionHandlers {
nextJump = jumps.next; nextJump = jumps.next;
if (prev instanceof EmptyBlock if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) { && prev.outer instanceof JsrBlock) {
JsrBlock jsr = (JsrBlock) prev.outer;
if (prev.outer == catchBlock) { if (prev.outer == catchBlock) {
/* This is the mandatory jsr in the catch block */ /* This is the mandatory jsr in the catch block */
continue; continue;
} }
/* We have a JSR to the subroutine, which is badly placed.
* We complain here.
*/
DescriptionBlock msg
= new DescriptionBlock("ERROR: JSR FINALLY BLOCK!");
tryFlow.removeSuccessor(jumps); tryFlow.removeSuccessor(jumps);
prev.removeJump(); prev.removeJump();
msg.replace(prev.outer); if (jsr.isGood()) {
StructuredBlock next = jsr.getNextBlock();
jsr.removeBlock();
if (next instanceof ReturnBlock)
removeReturnLocal((ReturnBlock) next);
} else {
/* We have a JSR to the subroutine, which is badly placed.
* We complain here.
*/
DescriptionBlock msg
= new DescriptionBlock("ERROR: JSR FINALLY BLOCK!");
msg.replace(prev.outer);
}
} else { } else {
/* We have a jump to the subroutine, that is wrong. /* We have a jump to the subroutine, that is wrong.
* We complain here. * We complain here.
@ -330,18 +339,15 @@ public class TransformExceptionHandlers {
StructuredBlock pred = skipFinExitChain(prev); StructuredBlock pred = skipFinExitChain(prev);
if (pred instanceof JsrBlock) { if (pred instanceof JsrBlock) {
StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; JsrBlock jsr = (JsrBlock) pred;
StructuredBlock jsrInner = jsr.innerBlock;
if (jsrInner instanceof EmptyBlock if (jsrInner instanceof EmptyBlock
&& jsrInner.jump != null && jsrInner.jump != null
&& jsrInner.jump.destination == subRoutine) { && jsrInner.jump.destination == subRoutine) {
/* The jump is preceeded by the right jsr. Remove /* The jump is preceeded by the right jsr. Mark the
* the jsr. * jsr as good.
*/ */
tryFlow.removeSuccessor(jsrInner.jump); jsr.setGood(true);
jsrInner.removeJump();
pred.removeBlock();
if (prev instanceof ReturnBlock)
removeReturnLocal((ReturnBlock) prev);
continue; continue;
} }
} }
@ -386,7 +392,8 @@ public class TransformExceptionHandlers {
} }
} }
} }
removeBadJSR(tryFlow, catchBlock, subRoutine); if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine);
} }
private void checkAndRemoveMonitorExit(FlowBlock tryFlow, private void checkAndRemoveMonitorExit(FlowBlock tryFlow,
@ -412,7 +419,8 @@ public class TransformExceptionHandlers {
} }
StructuredBlock pred = skipFinExitChain(prev); StructuredBlock pred = skipFinExitChain(prev);
if (pred instanceof JsrBlock) { if (pred instanceof JsrBlock) {
StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; JsrBlock jsr = (JsrBlock) pred;
StructuredBlock jsrInner = jsr.innerBlock;
if (jsrInner instanceof EmptyBlock if (jsrInner instanceof EmptyBlock
&& jsrInner.jump != null) { && jsrInner.jump != null) {
FlowBlock dest = jsrInner.jump.destination; FlowBlock dest = jsrInner.jump.destination;
@ -426,14 +434,10 @@ public class TransformExceptionHandlers {
} }
if (dest == subRoutine) { if (dest == subRoutine) {
/* The jump is preceeded by the right jsr. Remove /* The jump is preceeded by the right jsr.
* the jsr. * Mark it as good.
*/ */
tryFlow.removeSuccessor(jsrInner.jump); jsr.setGood(true);
jsrInner.removeJump();
pred.removeBlock();
if (prev instanceof ReturnBlock)
removeReturnLocal((ReturnBlock) prev);
continue; continue;
} }
} }
@ -498,7 +502,8 @@ public class TransformExceptionHandlers {
} }
if (subRoutine != null) { if (subRoutine != null) {
removeBadJSR(tryFlow, catchBlock, subRoutine); if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine);
tryFlow.mergeBlockNr(subRoutine); tryFlow.mergeBlockNr(subRoutine);
} }
} }

@ -1290,12 +1290,6 @@ public class CodeVerifier implements Opcodes {
Handler[] catchers = block.getCatchers(); Handler[] catchers = block.getCatchers();
if (catchers.length > 0) { if (catchers.length > 0) {
VerifyInfo excInfo = (VerifyInfo) info.clone(); VerifyInfo excInfo = (VerifyInfo) info.clone();
for (int j = 0; j < info.locals.length; j++) {
if (info.locals[j].getTypeSig().charAt(0) == 'N')
throw new VerifyException
("Uninitialized local in try block");
}
excInfo.stackHeight = 1; excInfo.stackHeight = 1;
for (int i=0; i < catchers.length; i++) { for (int i=0; i < catchers.length; i++) {
String type = catchers[i].getType(); String type = catchers[i].getType();
@ -1321,10 +1315,6 @@ public class CodeVerifier implements Opcodes {
if (catchers.length > 0 && instr.isStore()) { if (catchers.length > 0 && instr.isStore()) {
for (int i=0; i < catchers.length; i++) { for (int i=0; i < catchers.length; i++) {
int slot = instr.getLocalSlot(); int slot = instr.getLocalSlot();
if (info.locals[slot].getTypeSig().charAt(0) == 'N')
throw new VerifyException
("Uninitialized local in try block");
Block catcher = catchers[i].getCatcher(); Block catcher = catchers[i].getCatcher();
int catcherNr = catcher.getBlockNr(); int catcherNr = catcher.getBlockNr();
VerifyInfo oldInfo = verifyInfos[catcherNr]; VerifyInfo oldInfo = verifyInfos[catcherNr];

@ -33,6 +33,7 @@ import jode.type.Type;
import jode.type.MethodType; import jode.type.MethodType;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.io.IOException;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.Arrays; import java.util.Arrays;
@ -49,6 +50,8 @@ public class SyntheticAnalyzer implements Opcodes {
public final static int ACCESSPUTSTATIC = 6; public final static int ACCESSPUTSTATIC = 6;
public final static int ACCESSSTATICMETHOD = 7; public final static int ACCESSSTATICMETHOD = 7;
public final static int ACCESSCONSTRUCTOR = 8; public final static int ACCESSCONSTRUCTOR = 8;
public final static int ACCESSDUPPUTFIELD = 9;
public final static int ACCESSDUPPUTSTATIC = 10;
int kind = UNKNOWN; int kind = UNKNOWN;
@ -160,8 +163,7 @@ public class SyntheticAnalyzer implements Opcodes {
return true; return true;
} }
private final int modifierMask = (Modifier.PRIVATE | Modifier.PROTECTED | private final int modifierMask = Modifier.PUBLIC;
Modifier.PUBLIC);
/** /**
* Check if this is a field/method access method. We have only * Check if this is a field/method access method. We have only
@ -184,6 +186,7 @@ public class SyntheticAnalyzer implements Opcodes {
(succBlocks.length == 1 && succBlocks[0] != null)) (succBlocks.length == 1 && succBlocks[0] != null))
return false; return false;
Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator(); Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator();
boolean dupSeen = false;
if (!iter.hasNext()) if (!iter.hasNext())
return false; return false;
@ -209,13 +212,18 @@ public class SyntheticAnalyzer implements Opcodes {
if (params != 0) if (params != 0)
return false; return false;
Reference ref = instr.getReference(); Reference ref = instr.getReference();
String refClazz = ref.getClazz().substring(1); ClassInfo refClazz = TypeSignature
if (!(refClazz.substring(0, refClazz.length()-1) .getClassInfo(classInfo.getClassPath(), ref.getClazz());
.equals(classInfo.getName().replace('.','/')))) try {
if (!refClazz.superClassOf(classInfo))
return false;
} catch (IOException ex) {
/* Can't get enough info to ensure that refClazz is correct */
return false; return false;
}
FieldInfo refField FieldInfo refField
= classInfo.findField(ref.getName(), ref.getType()); = refClazz.findField(ref.getName(), ref.getType());
if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) if ((refField.getModifiers() & modifierMask) != 0)
return false; return false;
if (!iter.hasNext()) if (!iter.hasNext())
return false; return false;
@ -228,6 +236,16 @@ public class SyntheticAnalyzer implements Opcodes {
kind = (isStatic ? ACCESSGETSTATIC : ACCESSGETFIELD); kind = (isStatic ? ACCESSGETSTATIC : ACCESSGETFIELD);
return true; return true;
} }
if (instr.getOpcode() == (opc_dup - 3) + 3 * slot) {
/* This is probably a opc_dup or opc_dup2,
* preceding a opc_putfield
*/
instr = (Instruction) iter.next();
if (instr.getOpcode() != opc_putstatic
&& instr.getOpcode() != opc_putfield)
return false;
dupSeen = true;
}
if (instr.getOpcode() == opc_putfield if (instr.getOpcode() == opc_putfield
|| instr.getOpcode() == opc_putstatic) { || instr.getOpcode() == opc_putstatic) {
boolean isStatic = instr.getOpcode() == opc_putstatic; boolean isStatic = instr.getOpcode() == opc_putstatic;
@ -237,18 +255,30 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
/* For valid bytecode the type of param matches automatically */ /* For valid bytecode the type of param matches automatically */
Reference ref = instr.getReference(); Reference ref = instr.getReference();
String refClazz = ref.getClazz().substring(1); ClassInfo refClazz = TypeSignature
if (!(refClazz.substring(0, refClazz.length()-1) .getClassInfo(classInfo.getClassPath(), ref.getClazz());
.equals(classInfo.getName().replace('.','/')))) try {
if (!refClazz.superClassOf(classInfo))
return false;
} catch (IOException ex) {
/* Can't get enough info to ensure that refClazz is correct */
return false; return false;
}
FieldInfo refField FieldInfo refField
= classInfo.findField(ref.getName(), ref.getType()); = refClazz.findField(ref.getName(), ref.getType());
if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE) if ((refField.getModifiers() & modifierMask) != 0)
return false;
if (iter.hasNext())
return false; return false;
if (dupSeen) {
if (instr.getOpcode() < opc_ireturn
|| instr.getOpcode() > opc_areturn)
return false;
kind = (isStatic ? ACCESSDUPPUTSTATIC : ACCESSDUPPUTFIELD);
} else {
if (iter.hasNext())
return false;
kind = (isStatic ? ACCESSPUTSTATIC : ACCESSPUTFIELD);
}
reference = ref; reference = ref;
kind = (isStatic ? ACCESSPUTSTATIC : ACCESSPUTFIELD);
return true; return true;
} }
if (instr.getOpcode() == opc_invokestatic if (instr.getOpcode() == opc_invokestatic
@ -257,15 +287,20 @@ public class SyntheticAnalyzer implements Opcodes {
if (!isStatic) if (!isStatic)
params--; params--;
Reference ref = instr.getReference(); Reference ref = instr.getReference();
String refClazz = ref.getClazz().substring(1); ClassInfo refClazz = TypeSignature
if (!(refClazz.substring(0, refClazz.length()-1) .getClassInfo(classInfo.getClassPath(), ref.getClazz());
.equals(classInfo.getName().replace('.','/')))) try {
if (!refClazz.superClassOf(classInfo))
return false;
} catch (IOException ex) {
/* Can't get enough info to ensure that refClazz is correct */
return false; return false;
}
MethodInfo refMethod MethodInfo refMethod
= classInfo.findMethod(ref.getName(), ref.getType()); = refClazz.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(classInfo.getClassPath(), MethodType refType = Type.tMethod(classInfo.getClassPath(),
ref.getType()); ref.getType());
if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE if ((refMethod.getModifiers() & modifierMask) != 0
|| refType.getParameterTypes().length != params) || refType.getParameterTypes().length != params)
return false; return false;
if (refType.getReturnType() == Type.tVoid) { if (refType.getReturnType() == Type.tVoid) {
@ -336,15 +371,15 @@ public class SyntheticAnalyzer implements Opcodes {
} }
if (params > 0 && instr.getOpcode() == opc_invokespecial) { if (params > 0 && instr.getOpcode() == opc_invokespecial) {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
String refClazz = ref.getClazz().substring(1); ClassInfo refClazz = TypeSignature
if (!(refClazz.substring(0, refClazz.length()-1) .getClassInfo(classInfo.getClassPath(), ref.getClazz());
.equals(classInfo.getName().replace('.','/')))) if (refClazz != classInfo)
return false; return false;
MethodInfo refMethod MethodInfo refMethod
= classInfo.findMethod(ref.getName(), ref.getType()); = refClazz.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(classInfo.getClassPath(), MethodType refType = Type.tMethod(classInfo.getClassPath(),
ref.getType()); ref.getType());
if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE if ((refMethod.getModifiers() & modifierMask) != 0
|| !refMethod.getName().equals("<init>") || !refMethod.getName().equals("<init>")
|| unifyParam == -1 || unifyParam == -1
|| refType.getParameterTypes().length != params - 2) || refType.getParameterTypes().length != params - 2)

@ -1040,9 +1040,9 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
boolean known = value1.value != ConstValue.VOLATILE boolean known = value1.value != ConstValue.VOLATILE
&& value2.value != ConstValue.VOLATILE; && value2.value != ConstValue.VOLATILE;
if (known) { if (known) {
if ((opcode == opc_idiv if (((opcode == opc_idiv || opcode == opc_irem)
&& ((Integer)value2.value).intValue() == 0) && ((Integer)value2.value).intValue() == 0)
|| (opcode == opc_ldiv || ((opcode == opc_ldiv || opcode == opc_lrem)
&& ((Long)value2.value).longValue() == 0)) && ((Long)value2.value).longValue() == 0))
known = false; known = false;
} }

@ -362,20 +362,30 @@ public class Main
String cp = System.getProperty("java.class.path", ""); String cp = System.getProperty("java.class.path", "");
cp = cp.replace(File.pathSeparatorChar, cp = cp.replace(File.pathSeparatorChar,
Decompiler.altPathSeparatorChar); Decompiler.altPathSeparatorChar);
for (int i=0; i<params.length; i++) { String bootClassPath = System.getProperty("sun.boot.class.path");
if (bootClassPath != null)
cp += Decompiler.altPathSeparatorChar
+ bootClassPath.replace(File.pathSeparatorChar,
Decompiler.altPathSeparatorChar);
int i;
for (i = 0; i < params.length; i++) {
if (params[i].equals("--classpath") if (params[i].equals("--classpath")
|| params[i].equals("--cp") || params[i].equals("--cp")
|| params[i].equals("-c")) || params[i].equals("-c"))
cp = params[++i]; cp = params[++i];
else { else if (params[i].startsWith("-")) {
if (!params[i].equals("--help")
&& !params[i].equals("-h"))
System.err.println("Unknown option: "+params[i]);
usage(); usage();
return; return;
} } else
cp = params[i];
} }
StringTokenizer st = new StringTokenizer StringTokenizer st = new StringTokenizer
(cp, ""+Decompiler.altPathSeparatorChar); (cp, ""+Decompiler.altPathSeparatorChar);
String[] splitcp = new String[st.countTokens()]; String[] splitcp = new String[st.countTokens()];
for (int i = 0; i< splitcp.length; i++) for (i = 0; i< splitcp.length; i++)
splitcp[i] = st.nextToken(); splitcp[i] = st.nextToken();
Main win = new Main(splitcp); Main win = new Main(splitcp);
win.show(); win.show();

@ -1,8 +1,7 @@
usage.count=4 usage.count=3
usage.0 = usage: java jode.swingui.Main options* usage.0 = usage: java jode.swingui.Main [CLASSPATH]
usage.1 = \ -h, --help show this information. usage.1 = The directories in CLASSPATH should be separated by ','.
usage.2 = \ -c, --cp <path>, --classpath <path> search for classes in specified classpath. usage.2 = If no CLASSPATH is given the virtual machine classpath is used.
usage.3 = \ The directories must be separated by commas.
browse.filter.description = *.jar, *.zip Archives browse.filter.description = *.jar, *.zip Archives
browse.title = Browse browse.title = Browse

@ -1,8 +1,7 @@
usage.count=4 usage.count=3
usage.0 = Syntax: java jode.swingui.Main optionen* usage.0 = Syntax: java jode.swingui.Main [CLASSPATH]
usage.1 = \ -h, --help gib diese Hilfe aus usage.1 = Die Verzeichnisse in CLASSPATH werden durch Kommas abgetrennt.
usage.2 = \ -c, --cp <path>, --classpath <path> Suche die Klassen im gegebenen Classpath usage.2 = Wird kein CLASSPATH angegeben, so wird Java's standard classpath verwendet.
usage.3 = \ Die Verzeichnisse werden durch Kommas abgetrennt
browse.filter.description = *.jar, *.zip Archive browse.filter.description = *.jar, *.zip Archive
browse.title = Durchsuchen browse.title = Durchsuchen

@ -64,12 +64,18 @@ public class ArrayType extends ClassType {
} }
public Type getSuperType() { public Type getSuperType() {
return tRange(tObject, if (elementType instanceof IntegerType)
(ReferenceType) tArray(elementType.getSuperType())); return tRange(tObject, this);
else
return tRange(tObject,
(ReferenceType) tArray(elementType.getSuperType()));
} }
public Type getSubType() { public Type getSubType() {
return tArray(elementType.getSubType()); if (elementType instanceof IntegerType)
return this;
else
return tArray(elementType.getSubType());
} }
public Type getHint() { public Type getHint() {
@ -113,8 +119,9 @@ public class ArrayType extends ClassType {
*/ */
public Type getSpecializedType(Type type) { public Type getSpecializedType(Type type) {
/* /*
* tArray(x), iface -> tArray(x) iff tArray implements iface
* tArray(x), tArray(y) -> tArray(x.intersection(y)) * tArray(x), tArray(y) -> tArray(x.intersection(y))
* tArray(x), other -> tArray(x) iff tArray implements object * tArray(x), other -> tError
*/ */
if (type.getTypeCode() == TC_RANGE) { if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getBottom(); type = ((RangeType) type).getBottom();
@ -137,10 +144,10 @@ public class ArrayType extends ClassType {
* @return the common super type. * @return the common super type.
*/ */
public Type getGeneralizedType(Type type) { public Type getGeneralizedType(Type type) {
/* tArray(x), tNull -> tArray(x) /* tArray(x), tNull -> tArray(x)
* tArray(x), tClass(y) -> common ifaces of tArray and tClass * tArray(x), tClass(y) -> common ifaces of tArray and tClass
* tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject * tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject
* tArray(x), other -> tError * tArray(x), other -> tError
*/ */
if (type.getTypeCode() == TC_RANGE) { if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getTop(); type = ((RangeType) type).getTop();
@ -150,7 +157,9 @@ public class ArrayType extends ClassType {
if (type.getTypeCode() == TC_ARRAY) { if (type.getTypeCode() == TC_ARRAY) {
Type elType = elementType.intersection Type elType = elementType.intersection
(((ArrayType)type).elementType); (((ArrayType)type).elementType);
return elType != tError ? tArray(elType) : tObject; if (elType != tError)
return tArray(elType);
return MultiClassType.create(arrayIfaces);
} }
if (!(type instanceof ReferenceType)) if (!(type instanceof ReferenceType))
return tError; return tError;

@ -271,36 +271,57 @@ public abstract class ClassType extends ReferenceType {
private final static Hashtable keywords = new Hashtable(); private final static Hashtable keywords = new Hashtable();
static { static {
keywords.put("package", Boolean.TRUE); keywords.put("abstract", Boolean.TRUE);
keywords.put("import", Boolean.TRUE); keywords.put("default", Boolean.TRUE);
keywords.put("if", Boolean.TRUE); keywords.put("if", Boolean.TRUE);
keywords.put("else", Boolean.TRUE); keywords.put("private", Boolean.TRUE);
keywords.put("for", Boolean.TRUE);
keywords.put("while", Boolean.TRUE);
keywords.put("throw", Boolean.TRUE); keywords.put("throw", Boolean.TRUE);
keywords.put("return", Boolean.TRUE); keywords.put("boolean", Boolean.TRUE);
keywords.put("class", Boolean.TRUE); keywords.put("do", Boolean.TRUE);
keywords.put("interface", Boolean.TRUE);
keywords.put("implements", Boolean.TRUE); keywords.put("implements", Boolean.TRUE);
keywords.put("extends", Boolean.TRUE); keywords.put("protected", Boolean.TRUE);
keywords.put("throws", Boolean.TRUE);
keywords.put("break", Boolean.TRUE);
keywords.put("double", Boolean.TRUE);
keywords.put("import", Boolean.TRUE);
keywords.put("public", Boolean.TRUE);
keywords.put("transient", Boolean.TRUE);
keywords.put("byte", Boolean.TRUE);
keywords.put("else", Boolean.TRUE);
keywords.put("instanceof", Boolean.TRUE); keywords.put("instanceof", Boolean.TRUE);
keywords.put("new", Boolean.TRUE); keywords.put("return", Boolean.TRUE);
keywords.put("try", Boolean.TRUE);
keywords.put("case", Boolean.TRUE);
keywords.put("extends", Boolean.TRUE);
keywords.put("int", Boolean.TRUE); keywords.put("int", Boolean.TRUE);
keywords.put("boolean", Boolean.TRUE);
keywords.put("long", Boolean.TRUE);
keywords.put("float", Boolean.TRUE);
keywords.put("double", Boolean.TRUE);
keywords.put("short", Boolean.TRUE); keywords.put("short", Boolean.TRUE);
keywords.put("public", Boolean.TRUE); keywords.put("void", Boolean.TRUE);
keywords.put("protected", Boolean.TRUE); keywords.put("catch", Boolean.TRUE);
keywords.put("private", Boolean.TRUE); keywords.put("final", Boolean.TRUE);
keywords.put("interface", Boolean.TRUE);
keywords.put("static", Boolean.TRUE); keywords.put("static", Boolean.TRUE);
keywords.put("synchronized", Boolean.TRUE);
keywords.put("strict", Boolean.TRUE);
keywords.put("transient", Boolean.TRUE);
keywords.put("abstract", Boolean.TRUE);
keywords.put("volatile", Boolean.TRUE); keywords.put("volatile", Boolean.TRUE);
keywords.put("final", Boolean.TRUE); keywords.put("char", Boolean.TRUE);
keywords.put("finally", Boolean.TRUE);
keywords.put("long", Boolean.TRUE);
keywords.put("super", Boolean.TRUE);
keywords.put("while", Boolean.TRUE);
keywords.put("class", Boolean.TRUE);
keywords.put("float", Boolean.TRUE);
keywords.put("native", Boolean.TRUE);
keywords.put("switch", Boolean.TRUE);
keywords.put("const", Boolean.TRUE);
keywords.put("for", Boolean.TRUE);
keywords.put("new", Boolean.TRUE);
keywords.put("synchronized", Boolean.TRUE);
keywords.put("continue", Boolean.TRUE);
keywords.put("goto", Boolean.TRUE);
keywords.put("package", Boolean.TRUE);
keywords.put("this", Boolean.TRUE);
keywords.put("strictfp", Boolean.TRUE);
keywords.put("null", Boolean.TRUE);
keywords.put("true", Boolean.TRUE);
keywords.put("false", Boolean.TRUE);
} }
/** /**

@ -107,13 +107,18 @@ public class RangeType extends Type {
/** /**
* Returns the hint type of this range type set. This returns the * Returns the hint type of this range type set. This returns the
* singleton set containing only the first top type, except if it * singleton set containing only the first top type, except if it
* is null and there is a unique bottom type, in which case it returns * is null and there is a unique bottom type, in which case it
* the bottom type. * returns the bottom type.
* @return the hint type. * @return the hint type.
*/ */
public Type getHint() { public Type getHint() {
return topType == tNull && bottomType.equals(bottomType.getHint()) Type bottomHint = bottomType.getHint();
? bottomType.getHint(): topType.getHint(); Type topHint = topType.getHint();
if (topType == tNull && bottomType.equals(bottomHint))
return bottomHint;
return topHint;
} }
/** /**

Loading…
Cancel
Save