Applied changes from the Jode-1.1 tree.

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1327 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 24 years ago
parent 9f97289a90
commit c30ac484c5
  1. 57
      jode/ChangeLog
  2. 5
      jode/THANKS
  3. 18
      jode/configure.in
  4. 2
      jode/jode/Makefile.am
  5. 2
      jode/jode/bytecode/ClassInfo.java
  6. 34
      jode/jode/bytecode/ClassPath.java
  7. 9
      jode/jode/decompiler/ClassAnalyzer.java
  8. 118
      jode/jode/decompiler/DeadCodeAnalysis.java
  9. 6
      jode/jode/decompiler/Decompiler.java
  10. 5
      jode/jode/decompiler/FieldAnalyzer.java
  11. 98
      jode/jode/decompiler/LocalInfo.java
  12. 183
      jode/jode/decompiler/Main.java
  13. 8
      jode/jode/decompiler/MethodAnalyzer.java
  14. 97
      jode/jode/decompiler/TabbedPrintWriter.java
  15. 18
      jode/jode/expr/Expression.java
  16. 4
      jode/jode/expr/IfThenElseOperator.java
  17. 53
      jode/jode/expr/InvokeOperator.java
  18. 11
      jode/jode/expr/PopOperator.java
  19. 1
      jode/jode/expr/UnaryOperator.java
  20. 3
      jode/jode/flow/TransformExceptionHandlers.java
  21. 4
      jode/jode/jvm/SyntheticAnalyzer.java
  22. 2
      jode/jode/obfuscator/ConstantRuntimeEnvironment.java
  23. 3
      jode/jode/obfuscator/modules/ConstantAnalyzer.java
  24. 2
      jode/jode/swingui/Main.java
  25. 11
      jode/jode/type/NullType.java

@ -0,0 +1,57 @@
2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patch from Java 1.1 tree:
* jode/expr/Expression.java.in (updateParentTypes): Call setType,
instead of merging the types. Other childs want to know about the
type change as well.
* jode/decompiler/LocalInfo.java (combineWith): Reorganized a bit,
but no changes.
* jode/expr/InvokeOperator.java.in (dumpExpression): Always print
the ThisOperator if a field is from a parent class of an outer
class is used. And always qualify the this operator if not
innermost.
2001-07-14 Jochen Hoenicke <jochen@gnu.org>
Applied patches from the Java 1.1 tree:
* jode/decompiler/TabbedPrintWriter.java: Better gnu style handling:
(openBraceClass) (closeBraceClass)
(openBraceNoIndent) (closeBraceNoIndent): new functions.
(closeBraceNoSpace): Removed.
* jode/decompiler/TabbedPrintWriter.java (GNU_SPACING): new constant.
(printOptionalSpace): Print space for GNU_SPACING.
* jode/decompiler/Options.java (setOptions): changed gnu style
to include GNU_SPACING.
* jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use
open/closeBraceClass.
* jode/decompiler/MethodAnalyzer.java.in (dumpSource): Use
open/closeBraceNoIndent. Call printOptionalSpace.
* jode/decompiler/InvokeOperator.java.in (dumpExpression):
Call printOptionalSpace, use open/closeBraceClass for inner
classes.
* jode/decompiler/UnaryOperator.java (dumpExpression): Call
printOptionalSpace.
Added pascal style from Rolf Howarth <rolf@squarebox.co.uk>
* jode/decompiler/Decompiler.java (setOption): detect pascal option.
* jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT):
new constant.
(openBrace, openBraceContinue, closeBrace, closeBraceNoSpace,
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.
* jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the
"= 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
the method analyzer.
(simplify): handle unifyParam.
* jode/expr/PopOperator.java (getBreakPenalty): return penalty of
inner expression. (dumpExpression): Call dumpExpression of
subexpression immediately without priority.

@ -0,0 +1,5 @@
Joe Bronkema <joseph.d.bronkema at lmco.com>
Rolf Howarth <rolf at squarebox.co.uk> for pascal indentaton style.
Erik Modén <Erik.Moden at emw.ericsson.se>
Martin Schmitz <m.schmitz at e-sign.com> for finding many bugs in the obfuscator.
zzzeek <classic at io.com>

@ -114,6 +114,13 @@ JODE_CHECK_CLASS(java.lang.Object, $CLASSLIB,
[ AC_MSG_RESULT(no) [ AC_MSG_RESULT(no)
AC_MSG_ERROR(Please specify location of core java class library) ]) AC_MSG_ERROR(Please specify location of core java class library) ])
AC_MSG_CHECKING(for java.lang.ref.WeakReference)
JODE_CHECK_CLASS(java.lang.ref.WeakReference, $CLASSLIB,
[ AC_MSG_RESULT(yes)
JCPPFLAGS="-DJDK12" ],
[ AC_MSG_RESULT(no)
JCPPFLAGS="-DJDK11" ])
AC_MSG_CHECKING(for collection classes) AC_MSG_CHECKING(for collection classes)
JODE_CHECK_CLASS(java.util.Set, $CLASSPATH:$CLASSLIB, JODE_CHECK_CLASS(java.util.Set, $CLASSPATH:$CLASSLIB,
[ COLLECTIONS="java.util" [ COLLECTIONS="java.util"
@ -155,6 +162,8 @@ else
fi fi
AC_SUBST(SWINGUI) AC_SUBST(SWINGUI)
JCPPFLAGS="$JCPPFLAGS -DCOLLECTIONS=$COLLECTIONS -DCOLLECTIONEXTRA=$COLLECTIONEXTRA -DJAVAX_SWING=$JAVAX_SWING"
AC_SUBST(CLASSPATH) AC_SUBST(CLASSPATH)
AC_SUBST(JAVAC) AC_SUBST(JAVAC)
AC_SUBST(JAR) AC_SUBST(JAR)
@ -176,4 +185,11 @@ bin/jode
bin/jode.bat bin/jode.bat
doc/Makefile doc/Makefile
test/Makefile, test/Makefile,
[chmod 755 bin/jode]) [chmod 755 bin/jode],
[for i in \$CONFIG_FILES; do
changequote(, )dnl
if [ \$i != \${i%.java} ]; then
changequote([, ])dnl
$PERL $srcdir/jcpp $JCPPFLAGS \$i
fi
done])

@ -1,6 +1,6 @@
## 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 = util bytecode type jvm expr flow decompiler @SWINGUI@ # obfuscator SUBDIRS = util bytecode type jvm expr flow decompiler @SWINGUI@ # obfuscator
JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
-subdir=$(subdir) -dependdir=$(top_builddir) \ -subdir=$(subdir) -dependdir=$(top_builddir) \

@ -736,7 +736,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
int version = input.readUnsignedShort(); int version = input.readUnsignedShort();
version |= input.readUnsignedShort() << 16; version |= input.readUnsignedShort() << 16;
if (version < (45 << 16 | 0) if (version < (45 << 16 | 0)
|| version > (46 << 16 | 0)) || version > (47 << 16 | 0))
throw new ClassFormatException("Wrong class version"); throw new ClassFormatException("Wrong class version");
/* constant pool */ /* constant pool */

@ -151,6 +151,10 @@ public class ClassPath {
return false; return false;
} }
} }
public String toString() {
return "reflection:";
}
} }
private class LocalPath extends Path { private class LocalPath extends Path {
@ -212,6 +216,10 @@ public class ClassPath {
} }
}; };
} }
public String toString() {
return dir.getName();
}
} }
private class ZipPath extends Path { private class ZipPath extends Path {
@ -351,6 +359,10 @@ public class ClassPath {
return direntries.elements(); return direntries.elements();
return null; return null;
} }
public String toString() {
return file.getName();
}
} }
private class URLPath extends Path { private class URLPath extends Path {
@ -396,6 +408,10 @@ public class ClassPath {
clazz.read(input, howMuch); clazz.read(input, howMuch);
return true; return true;
} }
public String toString() {
return base.toString();
}
} }
private Path[] paths; private Path[] paths;
@ -435,8 +451,8 @@ public class ClassPath {
* @see #ClassPath(String[] paths) * @see #ClassPath(String[] paths)
*/ */
public ClassPath(String path, ClassPath fallback) { public ClassPath(String path, ClassPath fallback) {
this(path);
this.fallback = fallback; this.fallback = fallback;
initPath(tokenizeClassPath(path));
} }
/** /**
@ -449,6 +465,10 @@ public class ClassPath {
* @see #ClassPath(String[] paths) * @see #ClassPath(String[] paths)
*/ */
public ClassPath(String path) { public ClassPath(String path) {
initPath(tokenizeClassPath(path));
}
private String[] tokenizeClassPath(String path) {
// Calculate a good approximation (rounded upwards) of the tokens // Calculate a good approximation (rounded upwards) of the tokens
// in this path. // in this path.
int length = 1; int length = 1;
@ -505,7 +525,7 @@ public class ClassPath {
tokens[i] = path.substring(ptr, next); tokens[i] = path.substring(ptr, next);
ptr = next; ptr = next;
} }
initPath(tokens); return tokens;
} }
private byte[] readURLZip(URLConnection conn) { private byte[] readURLZip(URLConnection conn) {
@ -826,4 +846,14 @@ public class ClassPath {
return fallback.loadClass(clazz, howMuch); return fallback.loadClass(clazz, howMuch);
return false; return false;
} }
public String toString() {
StringBuffer sb = new StringBuffer("ClassPath[");
for (int i = 0; i < paths.length; i++) {
if (paths[i] != null)
sb.append(paths[i]).append(',');
}
sb.append(fallback).append(']');
return sb.toString();
}
} }

@ -604,15 +604,12 @@ public class ClassAnalyzer
} }
writer.println(); writer.println();
writer.openBrace(); writer.openBraceClass();
writer.tab(); writer.tab();
dumpBlock(writer, pl, done, scale); dumpBlock(writer, pl, done, scale);
writer.untab(); writer.untab();
if (parent instanceof MethodAnalyzer) { writer.closeBraceClass();
/* This is a method scope class */ writer.println();
writer.closeBraceNoSpace();
} else
writer.closeBrace();
clazz.drop(clazz.DECLARATIONS); clazz.drop(clazz.DECLARATIONS);
} }

@ -1,118 +0,0 @@
/* DeadCodeAnalysis Copyright (C) 1999 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 jode.decompiler;
import jode.bytecode.BytecodeInfo;
import jode.bytecode.Instruction;
import jode.bytecode.Handler;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
public class DeadCodeAnalysis {
private final static String REACHABLE = "R";
private final static String REACHCHANGED = "C";
private static void propagateReachability(BytecodeInfo code) {
boolean changed;
do {
changed = false;
for (Iterator iter = code.getInstructions().iterator();
iter.hasNext(); ) {
Instruction instr = (Instruction) iter.next();
if (instr.getTmpInfo() == REACHCHANGED) {
changed = true;
instr.setTmpInfo(REACHABLE);
Instruction[] succs = instr.getSuccs();
if (succs != null)
for (int i=0; i< succs.length; i++)
if (succs[i].getTmpInfo() == null)
succs[i].setTmpInfo(REACHCHANGED);
if (!instr.doesAlwaysJump()
&& instr.getNextByAddr() != null)
if (instr.getNextByAddr().getTmpInfo() == null)
instr.getNextByAddr().setTmpInfo(REACHCHANGED);
/*XXX code after jsr reachable iff ret is reachable...*/
if (instr.getOpcode() == Opcodes.opc_jsr)
if (instr.getNextByAddr().getTmpInfo() == null)
instr.getNextByAddr().setTmpInfo(REACHCHANGED);
}
}
} while (changed);
}
public static void removeDeadCode(BytecodeInfo code) {
((Instruction) code.getInstructions().get(0)).setTmpInfo(REACHCHANGED);
propagateReachability(code);
Handler[] handlers = code.getExceptionHandlers();
boolean changed;
do {
changed = false;
for (int i=0; i < handlers.length; i++) {
if (handlers[i].catcher.getTmpInfo() == null) {
/* check if the try block is somewhere reachable
* and mark the catcher as reachable then.
*/
for (Instruction instr = handlers[i].start;
instr != null; instr = instr.getNextByAddr()) {
if (instr.getTmpInfo() != null) {
handlers[i].catcher.setTmpInfo(REACHCHANGED);
propagateReachability(code);
changed = true;
break;
}
if (instr == handlers[i].end)
break;
}
}
}
} while (changed);
for (int i=0; i< handlers.length; i++) {
/* A handler is not reachable iff the catcher is not reachable */
if (handlers[i].catcher.getTmpInfo() == null) {
/* This is very seldom, so we can make it slow */
Handler[] newHandlers = new Handler[handlers.length - 1];
System.arraycopy(handlers, 0, newHandlers, 0, i);
System.arraycopy(handlers, i+1, newHandlers, i,
handlers.length - (i+1));
handlers = newHandlers;
code.setExceptionHandlers(newHandlers);
i--;
} else {
/* This works! */
while (handlers[i].start.getTmpInfo() == null)
handlers[i].start = handlers[i].start.getNextByAddr();
while (handlers[i].end.getTmpInfo() == null)
handlers[i].end = handlers[i].end.getPrevByAddr();
}
}
/* Now remove the dead code and clean up tmpInfo */
for (Iterator i = code.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
if (instr.getTmpInfo() != null)
instr.setTmpInfo(null);
else
i.remove();
}
}
}

@ -116,11 +116,15 @@ public class Decompiler {
public void setOption(String option, String value) { public void setOption(String option, String value) {
if (option.equals("style")) { if (option.equals("style")) {
if (value.equals("gnu")) { if (value.equals("gnu")) {
outputStyle = 0; outputStyle = TabbedPrintWriter.GNU_SPACING
| TabbedPrintWriter.INDENT_BRACES;
indentSize = 2; indentSize = 2;
} else if (value.equals("sun")) { } else if (value.equals("sun")) {
outputStyle = TabbedPrintWriter.BRACE_AT_EOL; outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
indentSize = 4; indentSize = 4;
} else if (value.equals("pascal")) {
outputStyle = 0;
indentSize = 4;
} else } else
throw new IllegalArgumentException("Invalid style "+value); throw new IllegalArgumentException("Invalid style "+value);
return; return;

@ -189,11 +189,6 @@ public class FieldAnalyzer implements Analyzer {
writer.breakOp(); writer.breakOp();
writer.print(" = "); writer.print(" = ");
constant.dumpExpression(writer.IMPL_PAREN, writer); constant.dumpExpression(writer.IMPL_PAREN, writer);
} else if ((modifiers & (Modifier.STATIC | Modifier.FINAL))
== (Modifier.STATIC | Modifier.FINAL)) {
/* Static final fields must always be initialized */
writer.breakOp();
writer.print(" = null;");
} }
writer.endOp(); writer.endOp();
writer.println(";"); writer.println(";");

@ -126,56 +126,58 @@ public class LocalInfo implements Declarable {
* If this is called with ourself nothing will happen. * If this is called with ourself nothing will happen.
* @param li the local info that we want to shadow. * @param li the local info that we want to shadow.
*/ */
public void combineWith(LocalInfo li) { public void combineWith(LocalInfo shadow) {
li = li.getLocalInfo(); if (this.shadow != null) {
if (shadow != null) { getLocalInfo().combineWith(shadow);
getLocalInfo().combineWith(li); return;
} else { }
if (this != li) {
shadow = li;
if (!nameIsGenerated)
shadow.name = name;
if (constExpr != null) {
if (shadow.constExpr != null)
throw new jode.AssertError
("local has multiple constExpr");
shadow.constExpr = constExpr;
}
// GlobalOptions.err.println("combining "+name+"("+type+") and " shadow = shadow.getLocalInfo();
// +shadow.name+"("+shadow.type+")"); if (this == shadow)
shadow.setType(type); return;
this.shadow = shadow;
boolean needTypeUpdate = !li.type.equals(type); if (!nameIsGenerated)
shadow.name = name;
java.util.Enumeration enum = operators.elements(); if (constExpr != null) {
while (enum.hasMoreElements()) { if (shadow.constExpr != null)
LocalVarOperator lvo = throw new jode.AssertError
(LocalVarOperator) enum.nextElement(); ("local has multiple constExpr");
if (needTypeUpdate) { shadow.constExpr = constExpr;
if ((GlobalOptions.debuggingFlags }
& GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("updating " + lvo);
lvo.updateType();
}
shadow.operators.addElement(lvo);
}
enum = hints.elements();
while (enum.hasMoreElements()) {
Object hint = enum.nextElement();
if (!shadow.hints.contains(hint))
shadow.hints.addElement(hint);
}
/* Clear unused fields, to allow garbage collection. // GlobalOptions.err.println("combining "+name+"("+type+") and "
*/ // +shadow.name+"("+shadow.type+")");
type = null; shadow.setType(type);
name = null;
operators = null;
} boolean needTypeUpdate = !shadow.type.equals(type);
}
java.util.Enumeration enum = operators.elements();
while (enum.hasMoreElements()) {
LocalVarOperator lvo =
(LocalVarOperator) enum.nextElement();
if (needTypeUpdate) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("updating " + lvo);
lvo.updateType();
}
shadow.operators.addElement(lvo);
}
enum = hints.elements();
while (enum.hasMoreElements()) {
Object hint = enum.nextElement();
if (!shadow.hints.contains(hint))
shadow.hints.addElement(hint);
}
/* Clear unused fields, to allow garbage collection.
*/
type = null;
name = null;
operators = null;
} }
/** /**

@ -29,7 +29,9 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.Enumeration;
import gnu.getopt.LongOpt; import gnu.getopt.LongOpt;
import gnu.getopt.Getopt; import gnu.getopt.Getopt;
@ -48,6 +50,7 @@ public class Main extends Options {
new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'),
new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'),
new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'), new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'),
new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'),
new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null,
OPTION_START+0), OPTION_START+0),
new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null,
@ -73,7 +76,10 @@ public class Main extends Options {
public static void usage() { public static void usage() {
PrintWriter err = GlobalOptions.err; PrintWriter err = GlobalOptions.err;
err.println("Version: " + GlobalOptions.version); err.println("Version: " + GlobalOptions.version);
err.println("Usage: java jode.decompiler.Main [OPTIONS]... [CLASSES]..."); err.println("Usage: java jode.decompiler.Main [OPTION]* {CLASS|JAR}*");
err.println("Give a fully qualified CLASS name, e.g. jode.decompiler.Main, if you want to");
err.println("decompile a single class, or a JAR file containing many classes.");
err.println("OPTION is any of these:");
err.println(" -h, --help "+ err.println(" -h, --help "+
"show this information."); "show this information.");
err.println(" -V, --version "+ err.println(" -V, --version "+
@ -86,6 +92,15 @@ public class Main extends Options {
"The directories should be separated by ','."); "The directories should be separated by ','.");
err.println(" -d, --dest <dir> "+ err.println(" -d, --dest <dir> "+
"write decompiled files to disk into directory destdir."); "write decompiled files to disk into directory destdir.");
err.println(" -s, --style {sun|gnu} "+
"specify indentation style");
err.println(" -i, --import <pkglimit>,<clslimit>");
err.println(" "+
"import classes used more than clslimit times");
err.println(" "+
"and packages with more then pkglimit used classes.");
err.println(" "+
"Limit 0 means never import. Default is 0,1.");
} }
public static boolean handleOption(int option, int longind, String arg) { public static boolean handleOption(int option, int longind, String arg) {
@ -104,6 +119,67 @@ public class Main extends Options {
return true; return true;
} }
public static void decompileClass(String className, ClassPath classPath,
String classPathStr,
ZipOutputStream destZip, String destDir,
TabbedPrintWriter writer,
ImportHandler imports) {
try {
ClassInfo clazz;
try {
clazz = classPath.getClassInfo(className);
} catch (IllegalArgumentException ex) {
GlobalOptions.err.println
("`"+className+"' is not a class name");
return;
}
if (skipClass(clazz))
return;
String filename =
className.replace('.', File.separatorChar)+".java";
if (destZip != null) {
writer.flush();
destZip.putNextEntry(new ZipEntry(filename));
} else if (destDir != null) {
File file = new File (destDir, filename);
File directory = new File(file.getParent());
if (!directory.exists() && !directory.mkdirs()) {
GlobalOptions.err.println
("Could not create directory "
+ directory.getPath() + ", check permissions.");
}
writer = new TabbedPrintWriter
(new BufferedOutputStream(new FileOutputStream(file)),
imports, false);
}
GlobalOptions.err.println(className);
ClassAnalyzer clazzAna = new ClassAnalyzer(clazz, imports);
clazzAna.dumpJavaFile(writer);
if (destZip != null) {
writer.flush();
destZip.closeEntry();
} else if (destDir != null)
writer.close();
/* Now is a good time to clean up */
System.gc();
} catch (FileNotFoundException ex) {
GlobalOptions.err.println
("Can't read "+ex.getMessage()+".");
GlobalOptions.err.println
("Check the class path ("+classPathStr+
") and check that you use the java class name.");
} catch (IOException ex) {
GlobalOptions.err.println
("Can't write source of "+className+".");
GlobalOptions.err.println("Check the permissions.");
ex.printStackTrace(GlobalOptions.err);
}
}
public static void main(String[] params) { public static void main(String[] params) {
if (params.length == 0) { if (params.length == 0) {
usage(); usage();
@ -111,12 +187,21 @@ public class Main extends Options {
} }
ClassPath classPath; ClassPath classPath;
String classPathStr = System.getProperty("java.class.path") String classPathStr = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, ClassPath.altPathSeparatorChar); .replace(File.pathSeparatorChar, ClassPath.altPathSeparatorChar);
String bootClassPath = System.getProperty("sun.boot.class.path");
if (bootClassPath != null)
classPathStr = classPathStr + ClassPath.altPathSeparatorChar
+ bootClassPath.replace(File.pathSeparatorChar,
ClassPath.altPathSeparatorChar);
String destDir = null; String destDir = null;
int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;; int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;;
int outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
int indentSize = 4;
GlobalOptions.err.println(GlobalOptions.copyright); GlobalOptions.err.println(GlobalOptions.copyright);
@ -163,6 +248,25 @@ public class Main extends Options {
errorInParams |= !GlobalOptions.setDebugging(arg); errorInParams |= !GlobalOptions.setDebugging(arg);
break; break;
} }
case 's': {
String arg = g.getOptarg();
if (arg.equals("gnu")) {
outputStyle = TabbedPrintWriter.GNU_SPACING
| TabbedPrintWriter.INDENT_BRACES;
indentSize = 2;
} else if (arg.equals("sun")) {
outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
indentSize = 4;
} else if (arg.equals("pascal")) {
outputStyle = 0;
indentSize = 4;
} else {
GlobalOptions.err.println
("jode.decompiler.Main: Unknown style `"+arg+"'.");
errorInParams = true;
}
break;
}
case 'i': { case 'i': {
String arg = g.getOptarg(); String arg = g.getOptarg();
int comma = arg.indexOf(','); int comma = arg.indexOf(',');
@ -208,7 +312,8 @@ public class Main extends Options {
ZipOutputStream destZip = null; ZipOutputStream destZip = null;
TabbedPrintWriter writer = null; TabbedPrintWriter writer = null;
if (destDir == null) if (destDir == null)
writer = new TabbedPrintWriter(System.out, imports); writer = new TabbedPrintWriter(System.out, imports, true,
outputStyle, indentSize, 0, 79);
else if (destDir.toLowerCase().endsWith(".zip") else if (destDir.toLowerCase().endsWith(".zip")
|| destDir.toLowerCase().endsWith(".jar")) { || destDir.toLowerCase().endsWith(".jar")) {
try { try {
@ -219,61 +324,37 @@ public class Main extends Options {
return; return;
} }
writer = new TabbedPrintWriter(new BufferedOutputStream(destZip), writer = new TabbedPrintWriter(new BufferedOutputStream(destZip),
imports, false); imports, false,
outputStyle, indentSize, 0, 79);
} }
for (int i= g.getOptind(); i< params.length; i++) { for (int i= g.getOptind(); i< params.length; i++) {
try { try {
ClassInfo clazz; if ((params[i].endsWith(".jar") || params[i].endsWith(".zip"))
try { && new File(params[i]).isFile()) {
clazz = classPath.getClassInfo(params[i]); /* The user obviously wants to decompile a jar/zip file.
} catch (IllegalArgumentException ex) { * Lets do him a pleasure and allow this.
GlobalOptions.err.println */
("`"+params[i]+"' is not a class name"); ClassPath zipClassPath
continue; = new ClassPath(params[i], classPath);
} Enumeration enum = new ZipFile(params[i]).entries();
if (skipClass(clazz)) while (enum.hasMoreElements()) {
continue; String entry
= ((ZipEntry) enum.nextElement()).getName();
String filename = if (entry.endsWith(".class")) {
params[i].replace('.', File.separatorChar)+".java"; entry = entry.substring(0, entry.length() - 6)
if (destZip != null) { .replace('/', '.');
writer.flush(); decompileClass(entry, zipClassPath, classPathStr,
destZip.putNextEntry(new ZipEntry(filename)); destZip, destDir,
} else if (destDir != null) { writer, imports);
File file = new File (destDir, filename); }
File directory = new File(file.getParent());
if (!directory.exists() && !directory.mkdirs()) {
GlobalOptions.err.println
("Could not create directory "
+ directory.getPath() + ", check permissions.");
} }
writer = new TabbedPrintWriter } else
(new BufferedOutputStream(new FileOutputStream(file)), decompileClass(params[i], classPath, classPathStr,
imports, false); destZip, destDir,
} writer, imports);
GlobalOptions.err.println(params[i]);
ClassAnalyzer clazzAna = new ClassAnalyzer(clazz, imports);
clazzAna.dumpJavaFile(writer);
if (destZip != null) {
writer.flush();
destZip.closeEntry();
} else if (destDir != null)
writer.close();
/* Now is a good time to clean up */
System.gc();
} catch (FileNotFoundException ex) {
GlobalOptions.err.println
("Can't read "+ex.getMessage()+".");
GlobalOptions.err.println
("Check the class path ("+classPathStr+
") 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 "+params[i]+"."); ("Can't read zip file " + params[i] + ".");
GlobalOptions.err.println("Check the permissions.");
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(GlobalOptions.err);
} }
} }

@ -833,6 +833,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.print(" " + methodName); writer.print(" " + methodName);
} }
writer.breakOp(); writer.breakOp();
writer.printOptionalSpace();
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0); writer.startOp(writer.EXPL_PAREN, 0);
int offset = skipParams + (isStatic() ? 0 : 1); int offset = skipParams + (isStatic() ? 0 : 1);
@ -862,11 +863,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
} }
writer.endOp(); writer.endOp();
if (bb != null) { if (bb != null) {
writer.openBrace(); writer.openBraceNoIndent();
writer.tab(); writer.tab();
methodHeader.dumpSource(writer); methodHeader.dumpSource(writer);
writer.untab(); writer.untab();
writer.closeBrace(); writer.closeBraceNoIndent();
} else } else
writer.println(";"); writer.println(";");
writer.popScope(); writer.popScope();
@ -975,8 +976,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
expr).getSubExpressions()[0]; expr).getSubExpressions()[0];
if (expr instanceof ThisOperator) { if (expr instanceof ThisOperator) {
outerValueArray[j] = outerValueArray[j] =
new ThisOperator(((ThisOperator) new ThisOperator(((ThisOperator) expr).getClassInfo());
expr).getClassInfo());
continue; continue;
} }
LocalInfo li = null; LocalInfo li = null;

@ -40,7 +40,9 @@ public class TabbedPrintWriter {
private ImportHandler imports; private ImportHandler imports;
private Stack scopes = new Stack(); private Stack scopes = new Stack();
public static final int BRACE_AT_EOL = 0x10; public static final int BRACE_AT_EOL = 0x10;
public static final int INDENT_BRACES = 0x20;
public static final int GNU_SPACING = 0x40;
/** /**
* This string contains a few tab characters followed by tabWidth - 1 * This string contains a few tab characters followed by tabWidth - 1
@ -132,6 +134,7 @@ public class TabbedPrintWriter {
* our child, if possible. * our child, if possible.
*/ */
BreakPoint child = (BreakPoint) childBPs.elementAt(0); BreakPoint child = (BreakPoint) childBPs.elementAt(0);
options = child.options;
startPos = child.startPos; startPos = child.startPos;
options = child.options; options = child.options;
endPos = child.endPos; endPos = child.endPos;
@ -728,69 +731,91 @@ public class TabbedPrintWriter {
return type.toString(); return type.toString();
} }
public void printOptionalSpace() {
if ((style & GNU_SPACING) != 0)
print(" ");
}
/** /**
* Print a opening brace with the current indentation style. * Print a opening brace with the current indentation style.
* Called at the end of the line of the instance that opens the * Called at the end of the line of the instance that opens the
* brace. It doesn't do a tab stop after opening the brace. * brace. It doesn't do a tab stop after opening the brace.
*/ */
public void openBrace() { public void openBrace() {
if ((style & BRACE_AT_EOL) != 0) { boolean bracePrinted = false;
print(currentLine.length() > 0 ? " {" : "{"); if (currentLine.length() > 0) {
if ((style & BRACE_AT_EOL) != 0) {
print(" {");
bracePrinted = true;
}
println(); println();
} else {
if (currentLine.length() > 0)
println();
if (currentIndent > 0)
tab();
println("{");
} }
if ((style & INDENT_BRACES) != 0 && currentIndent > 0)
tab();
if (!bracePrinted)
println("{");
}
public void openBraceClass() {
openBraceNoIndent();
} }
/** /**
* Print a opening brace with the current indentation style. * Print a opening brace with the current indentation style.
* Called at the end the line of a method declaration.
*/
public void openBraceNoIndent() {
if (currentLine.length() > 0) {
if ((style & BRACE_AT_EOL) != 0)
print(" ");
else
println();
}
println("{");
}
/**
* Print an opening brace with the current indentation style.
* Called at the end of the line of the instance that opens the * Called at the end of the line of the instance that opens the
* brace. It doesn't do a tab stop after opening the brace. * brace. It doesn't do a tab stop after opening the brace.
*/ */
public void openBraceNoSpace() { public void openBraceNoSpace() {
if ((style & BRACE_AT_EOL) != 0) boolean bracePrinted = false;
println("{"); if (currentLine.length() > 0) {
else { if ((style & BRACE_AT_EOL) != 0) {
if (currentLine.length() > 0) print("{");
println(); bracePrinted = true;
if (currentIndent > 0) }
tab(); println();
println("{");
} }
if ((style & INDENT_BRACES) != 0 && currentIndent > 0)
tab();
if (!bracePrinted)
println("{");
} }
public void closeBraceContinue() { public void closeBraceContinue() {
if ((style & BRACE_AT_EOL) != 0) if ((style & BRACE_AT_EOL) != 0)
print("} "); print("} ");
else { else
println("}"); println("}");
if (currentIndent > 0) if ((style & INDENT_BRACES) != 0 && currentIndent > 0)
untab(); untab();
}
} }
public void closeBraceNoSpace() { public void closeBraceClass() {
if ((style & BRACE_AT_EOL) != 0) print("}");
print("}");
else {
println("}");
if (currentIndent > 0)
untab();
}
} }
public void closeBrace() { public void closeBrace() {
if ((style & BRACE_AT_EOL) != 0) println("}");
println("}"); if ((style & INDENT_BRACES) != 0 && currentIndent > 0)
else { untab();
println("}"); }
if (currentIndent > 0)
untab(); public void closeBraceNoIndent() {
} println("}");
} }
public void flush() { public void flush() {

@ -55,23 +55,7 @@ public abstract class Expression {
} }
public void updateParentType(Type otherType) { public void updateParentType(Type otherType) {
Type newType = otherType.intersection(type); setType(otherType);
if (type.equals(newType))
return;
if (newType == Type.tError) {
if (otherType == Type.tError) {
// Don't propagate type errors.
return;
}
GlobalOptions.err.println("updateParentType: Type error in "
+this+": merging "+getType()
+" and "+otherType);
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0)
Thread.dumpStack();
}
type = newType;
if (parent != null) if (parent != null)
parent.updateType(); parent.updateType();
} }

@ -40,9 +40,9 @@ public class IfThenElseOperator extends Operator {
} }
public void updateType() { public void updateType() {
Type subType = Type.tSuperType(subExpressions[1].getType()) Type commonType = Type.tSuperType(subExpressions[1].getType())
.intersection(Type.tSuperType(subExpressions[2].getType())); .intersection(Type.tSuperType(subExpressions[2].getType()));
updateParentType(subType); updateParentType(commonType);
} }
public Expression simplify() { public Expression simplify() {

@ -242,7 +242,7 @@ public final class InvokeOperator extends Operator
} }
/** /**
* Makes a non void expression out of this store instruction. * Makes a non void expression, in case this is a constructor.
*/ */
public void makeNonVoid() { public void makeNonVoid() {
if (type != Type.tVoid) if (type != Type.tVoid)
@ -417,12 +417,12 @@ public final class InvokeOperator extends Operator
* @return true if this is the magic class$ method, false otherwise. * @return true if this is the magic class$ method, false otherwise.
*/ */
public boolean isGetClass() { public boolean isGetClass() {
if (isThis()) { MethodAnalyzer mana = getMethodAnalyzer();
SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); if (mana == null)
if (synth != null && synth.getKind() == SyntheticAnalyzer.GETCLASS) return false;
return true; SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
} return (synth != null
return false; && synth.getKind() == SyntheticAnalyzer.GETCLASS);
} }
class Environment extends SimpleRuntimeEnvironment { class Environment extends SimpleRuntimeEnvironment {
@ -712,9 +712,10 @@ public final class InvokeOperator extends Operator
} }
for (int p = offset; p < paramTypes.length; p++) { for (int p = offset; p < paramTypes.length; p++) {
if (!paramTypes[p] if (!paramTypes[p]
.isOfType(Type.tSubType(otherParamTypes[p-offset]))) .isOfType(Type.tSubType(otherParamTypes[p-offset]))){
/* No conflict here */ /* No conflict here */
continue next_method; continue next_method;
}
} }
/* There is a conflict that can be resolved by a cast. */ /* There is a conflict that can be resolved by a cast. */
return true; return true;
@ -1100,30 +1101,19 @@ public final class InvokeOperator extends Operator
ThisOperator thisOp = (ThisOperator) subExpressions[0]; ThisOperator thisOp = (ThisOperator) subExpressions[0];
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
* an outer class, or it is inherited from the
* parent of this class and there is a conflicting
* field in some outer class.
*/
getMethodAnalyzer() == null
&& (!isThis() ||
writer.conflicts(methodName, null,
Scope.NOSUPERMETHODNAME)))) {
thisOp.dumpExpression(writer, 950); thisOp.dumpExpression(writer, 950);
writer.breakOp(); writer.breakOp();
writer.print("."); writer.print(".");
} else if (/* This is a inherited field conflicting
* with a field name in some outer class.
*/
getMethodAnalyzer() == null
&& writer.conflicts(methodName, null,
Scope.NOSUPERMETHODNAME)) {
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.print(".");
} }
} else { } else {
if (needsCast(0, paramTypes)){ if (needsCast(0, paramTypes)){
@ -1150,6 +1140,7 @@ public final class InvokeOperator extends Operator
* We still need to check for casts though. * We still need to check for casts though.
*/ */
writer.breakOp(); writer.breakOp();
writer.printOptionalSpace();
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0); writer.startOp(writer.EXPL_PAREN, 0);
boolean first = true; boolean first = true;
@ -1183,11 +1174,11 @@ public final class InvokeOperator extends Operator
*/ */
if (anonymousNew) { if (anonymousNew) {
Object state = writer.saveOps(); Object state = writer.saveOps();
writer.openBrace(); writer.openBraceClass();
writer.tab(); writer.tab();
clazzAna.dumpBlock(writer); clazzAna.dumpBlock(writer);
writer.untab(); writer.untab();
writer.closeBraceNoSpace(); writer.closeBraceClass();
writer.restoreOps(state); writer.restoreOps(state);
} }
} }

@ -41,8 +41,17 @@ public class PopOperator extends Operator {
public void updateType() { public void updateType() {
} }
public int getBreakPenalty() {
if (subExpressions[0] instanceof Operator)
return ((Operator) subExpressions[0]).getBreakPenalty();
return 0;
}
public void dumpExpression(TabbedPrintWriter writer) public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
subExpressions[0].dumpExpression(writer, 0); /* Don't give a priority; we can't allow parents around
* a statement.
*/
subExpressions[0].dumpExpression(writer);
} }
} }

@ -57,6 +57,7 @@ public class UnaryOperator extends Operator {
public void dumpExpression(TabbedPrintWriter writer) public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
writer.print(getOperatorString()); writer.print(getOperatorString());
writer.printOptionalSpace();
subExpressions[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

@ -772,9 +772,6 @@ public class TransformExceptionHandlers {
if (subRoutine != null) { if (subRoutine != null) {
while (subRoutine.analyze(tryFlow.getNextBlockNr(), end)); while (subRoutine.analyze(tryFlow.getNextBlockNr(), end));
System.err.println("Finally: "+subRoutine+
" Try: "+tryFlow+
" preds: "+subRoutine.predecessors);
/* Now check if the subroutine is correct and has only the /* Now check if the subroutine is correct and has only the
* catchFlow as predecessor. * catchFlow as predecessor.

@ -52,7 +52,7 @@ public class SyntheticAnalyzer implements Opcodes {
int kind = UNKNOWN; int kind = UNKNOWN;
int unifyParam; int unifyParam = -1;
Reference reference; Reference reference;
ClassInfo classInfo; ClassInfo classInfo;
MethodInfo method; MethodInfo method;
@ -334,7 +334,7 @@ public class SyntheticAnalyzer implements Opcodes {
params++; params++;
slot++; slot++;
} }
if (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); String refClazz = ref.getClazz().substring(1);
if (!(refClazz.substring(0, refClazz.length()-1) if (!(refClazz.substring(0, refClazz.length()-1)

@ -231,7 +231,7 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
if (fi != null && !fi.isNotConstant()) { if (fi != null && !fi.isNotConstant()) {
Object result = fi.getConstant(); Object result = fi.getConstant();
if (result == null) if (result == null)
result = getDefaultValue(ref.getType()); result = TypeSignature.getDefaultValue(ref.getType());
return result; return result;
} }
throw new InterpreterException("Field " + ref + " not constant"); throw new InterpreterException("Field " + ref + " not constant");

@ -1839,8 +1839,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
Handler[] newHandlers = new Handler[newHandlerCtr]; Handler[] newHandlers = new Handler[newHandlerCtr];
System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr); System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr);
System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr); System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr);
bb.setBlocks(newBlocks, newStartBlock); bb.setBlocks(newBlocks, newStartBlock, newHandlers);
bb.setExceptionHandlers(newHandlers);
} }
} }
} }

@ -104,9 +104,11 @@ public class Main
classTree.addTreeSelectionListener(this); classTree.addTreeSelectionListener(this);
JScrollPane spClassTree = new JScrollPane(classTree); JScrollPane spClassTree = new JScrollPane(classTree);
sourcecodeArea = new JTextArea(20, 80); sourcecodeArea = new JTextArea(20, 80);
sourcecodeArea.setEditable(false);
sourcecodeArea.setFont(monospaced); sourcecodeArea.setFont(monospaced);
JScrollPane spText = new JScrollPane(sourcecodeArea); JScrollPane spText = new JScrollPane(sourcecodeArea);
errorArea = new JTextArea(3, 80); errorArea = new JTextArea(3, 80);
errorArea.setEditable(false);
errorArea.setFont(monospaced); errorArea.setFont(monospaced);
JScrollPane spError = new JScrollPane(errorArea); JScrollPane spError = new JScrollPane(errorArea);

@ -83,15 +83,4 @@ public class NullType extends ReferenceType {
public Type findCommonClassTypes(Stack otherTypes) { public Type findCommonClassTypes(Stack otherTypes) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
/**
* Intersect this type with another type and return the new type.
* @param type the other type.
* @return the intersection, or tError, if a type conflict happens.
*/
public Type intersection(Type type) {
if (type == this)
return type;
return tError;
}
} }

Loading…
Cancel
Save