From c30ac484c5884001e486bd0b1920e4274bfc7ea3 Mon Sep 17 00:00:00 2001 From: hoenicke Date: Sun, 15 Jul 2001 15:06:32 +0000 Subject: [PATCH] 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 --- jode/ChangeLog | 57 ++++++ jode/THANKS | 5 + jode/configure.in | 18 +- jode/jode/Makefile.am | 2 +- jode/jode/bytecode/ClassInfo.java | 2 +- jode/jode/bytecode/ClassPath.java | 34 +++- jode/jode/decompiler/ClassAnalyzer.java | 9 +- jode/jode/decompiler/DeadCodeAnalysis.java | 118 ----------- jode/jode/decompiler/Decompiler.java | 6 +- jode/jode/decompiler/FieldAnalyzer.java | 5 - jode/jode/decompiler/LocalInfo.java | 100 +++++----- jode/jode/decompiler/Main.java | 183 +++++++++++++----- jode/jode/decompiler/MethodAnalyzer.java | 8 +- jode/jode/decompiler/TabbedPrintWriter.java | 97 ++++++---- jode/jode/expr/Expression.java | 18 +- jode/jode/expr/IfThenElseOperator.java | 4 +- jode/jode/expr/InvokeOperator.java | 53 +++-- jode/jode/expr/PopOperator.java | 11 +- jode/jode/expr/UnaryOperator.java | 1 + .../jode/flow/TransformExceptionHandlers.java | 3 - jode/jode/jvm/SyntheticAnalyzer.java | 4 +- .../ConstantRuntimeEnvironment.java | 2 +- .../obfuscator/modules/ConstantAnalyzer.java | 3 +- jode/jode/swingui/Main.java | 2 + jode/jode/type/NullType.java | 11 -- 25 files changed, 411 insertions(+), 345 deletions(-) create mode 100644 jode/THANKS delete mode 100644 jode/jode/decompiler/DeadCodeAnalysis.java diff --git a/jode/ChangeLog b/jode/ChangeLog index e69de29..c2ee591 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -0,0 +1,57 @@ +2001-07-15 Jochen Hoenicke + 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 + 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 + * 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. diff --git a/jode/THANKS b/jode/THANKS new file mode 100644 index 0000000..b95fa57 --- /dev/null +++ b/jode/THANKS @@ -0,0 +1,5 @@ +Joe Bronkema +Rolf Howarth for pascal indentaton style. +Erik Modén +Martin Schmitz for finding many bugs in the obfuscator. +zzzeek diff --git a/jode/configure.in b/jode/configure.in index 1074013..0fc33e2 100644 --- a/jode/configure.in +++ b/jode/configure.in @@ -114,6 +114,13 @@ JODE_CHECK_CLASS(java.lang.Object, $CLASSLIB, [ AC_MSG_RESULT(no) 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) JODE_CHECK_CLASS(java.util.Set, $CLASSPATH:$CLASSLIB, [ COLLECTIONS="java.util" @@ -155,6 +162,8 @@ else fi AC_SUBST(SWINGUI) +JCPPFLAGS="$JCPPFLAGS -DCOLLECTIONS=$COLLECTIONS -DCOLLECTIONEXTRA=$COLLECTIONEXTRA -DJAVAX_SWING=$JAVAX_SWING" + AC_SUBST(CLASSPATH) AC_SUBST(JAVAC) AC_SUBST(JAR) @@ -176,4 +185,11 @@ bin/jode bin/jode.bat doc/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]) diff --git a/jode/jode/Makefile.am b/jode/jode/Makefile.am index 15d43b2..5507214 100644 --- a/jode/jode/Makefile.am +++ b/jode/jode/Makefile.am @@ -1,6 +1,6 @@ ## 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 \ -subdir=$(subdir) -dependdir=$(top_builddir) \ diff --git a/jode/jode/bytecode/ClassInfo.java b/jode/jode/bytecode/ClassInfo.java index c51eb9b..3e9ddca 100644 --- a/jode/jode/bytecode/ClassInfo.java +++ b/jode/jode/bytecode/ClassInfo.java @@ -736,7 +736,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { int version = input.readUnsignedShort(); version |= input.readUnsignedShort() << 16; if (version < (45 << 16 | 0) - || version > (46 << 16 | 0)) + || version > (47 << 16 | 0)) throw new ClassFormatException("Wrong class version"); /* constant pool */ diff --git a/jode/jode/bytecode/ClassPath.java b/jode/jode/bytecode/ClassPath.java index e6c1c6e..3cd8038 100644 --- a/jode/jode/bytecode/ClassPath.java +++ b/jode/jode/bytecode/ClassPath.java @@ -151,6 +151,10 @@ public class ClassPath { return false; } } + + public String toString() { + return "reflection:"; + } } private class LocalPath extends Path { @@ -212,6 +216,10 @@ public class ClassPath { } }; } + + public String toString() { + return dir.getName(); + } } private class ZipPath extends Path { @@ -351,6 +359,10 @@ public class ClassPath { return direntries.elements(); return null; } + + public String toString() { + return file.getName(); + } } private class URLPath extends Path { @@ -396,6 +408,10 @@ public class ClassPath { clazz.read(input, howMuch); return true; } + + public String toString() { + return base.toString(); + } } private Path[] paths; @@ -435,8 +451,8 @@ public class ClassPath { * @see #ClassPath(String[] paths) */ public ClassPath(String path, ClassPath fallback) { - this(path); this.fallback = fallback; + initPath(tokenizeClassPath(path)); } /** @@ -449,6 +465,10 @@ public class ClassPath { * @see #ClassPath(String[] paths) */ public ClassPath(String path) { + initPath(tokenizeClassPath(path)); + } + + private String[] tokenizeClassPath(String path) { // Calculate a good approximation (rounded upwards) of the tokens // in this path. int length = 1; @@ -505,7 +525,7 @@ public class ClassPath { tokens[i] = path.substring(ptr, next); ptr = next; } - initPath(tokens); + return tokens; } private byte[] readURLZip(URLConnection conn) { @@ -826,4 +846,14 @@ public class ClassPath { return fallback.loadClass(clazz, howMuch); 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(); + } } diff --git a/jode/jode/decompiler/ClassAnalyzer.java b/jode/jode/decompiler/ClassAnalyzer.java index ca8e357..d3dabe4 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java +++ b/jode/jode/decompiler/ClassAnalyzer.java @@ -604,15 +604,12 @@ public class ClassAnalyzer } writer.println(); - writer.openBrace(); + writer.openBraceClass(); writer.tab(); dumpBlock(writer, pl, done, scale); writer.untab(); - if (parent instanceof MethodAnalyzer) { - /* This is a method scope class */ - writer.closeBraceNoSpace(); - } else - writer.closeBrace(); + writer.closeBraceClass(); + writer.println(); clazz.drop(clazz.DECLARATIONS); } diff --git a/jode/jode/decompiler/DeadCodeAnalysis.java b/jode/jode/decompiler/DeadCodeAnalysis.java deleted file mode 100644 index f524895..0000000 --- a/jode/jode/decompiler/DeadCodeAnalysis.java +++ /dev/null @@ -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(); - } - } -} diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java index f592a78..745f9df 100644 --- a/jode/jode/decompiler/Decompiler.java +++ b/jode/jode/decompiler/Decompiler.java @@ -116,11 +116,15 @@ public class Decompiler { public void setOption(String option, String value) { if (option.equals("style")) { if (value.equals("gnu")) { - outputStyle = 0; + outputStyle = TabbedPrintWriter.GNU_SPACING + | TabbedPrintWriter.INDENT_BRACES; indentSize = 2; } else if (value.equals("sun")) { outputStyle = TabbedPrintWriter.BRACE_AT_EOL; indentSize = 4; + } else if (value.equals("pascal")) { + outputStyle = 0; + indentSize = 4; } else throw new IllegalArgumentException("Invalid style "+value); return; diff --git a/jode/jode/decompiler/FieldAnalyzer.java b/jode/jode/decompiler/FieldAnalyzer.java index da30417..cb75705 100644 --- a/jode/jode/decompiler/FieldAnalyzer.java +++ b/jode/jode/decompiler/FieldAnalyzer.java @@ -189,11 +189,6 @@ public class FieldAnalyzer implements Analyzer { writer.breakOp(); writer.print(" = "); 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.println(";"); diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java index 82121b5..39a32df 100644 --- a/jode/jode/decompiler/LocalInfo.java +++ b/jode/jode/decompiler/LocalInfo.java @@ -126,56 +126,58 @@ public class LocalInfo implements Declarable { * If this is called with ourself nothing will happen. * @param li the local info that we want to shadow. */ - public void combineWith(LocalInfo li) { - li = li.getLocalInfo(); - if (shadow != null) { - getLocalInfo().combineWith(li); - } 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.name+"("+shadow.type+")"); - shadow.setType(type); - - - boolean needTypeUpdate = !li.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); - } + public void combineWith(LocalInfo shadow) { + if (this.shadow != null) { + getLocalInfo().combineWith(shadow); + return; + } - /* Clear unused fields, to allow garbage collection. - */ - type = null; - name = null; - operators = null; - } - } + shadow = shadow.getLocalInfo(); + if (this == shadow) + return; + + this.shadow = shadow; + 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.name+"("+shadow.type+")"); + shadow.setType(type); + + + 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; } /** diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java index c894518..d427e5c 100644 --- a/jode/jode/decompiler/Main.java +++ b/jode/jode/decompiler/Main.java @@ -29,7 +29,9 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.zip.ZipOutputStream; +import java.util.zip.ZipFile; import java.util.zip.ZipEntry; +import java.util.Enumeration; import gnu.getopt.LongOpt; import gnu.getopt.Getopt; @@ -48,6 +50,7 @@ public class Main extends Options { new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'), + new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'), new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null, OPTION_START+0), new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null, @@ -73,7 +76,10 @@ public class Main extends Options { public static void usage() { PrintWriter err = GlobalOptions.err; 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 "+ "show this information."); err.println(" -V, --version "+ @@ -86,6 +92,15 @@ public class Main extends Options { "The directories should be separated by ','."); err.println(" -d, --dest "+ "write decompiled files to disk into directory destdir."); + err.println(" -s, --style {sun|gnu} "+ + "specify indentation style"); + err.println(" -i, --import ,"); + 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) { @@ -104,6 +119,67 @@ public class Main extends Options { 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) { if (params.length == 0) { usage(); @@ -111,12 +187,21 @@ public class Main extends Options { } ClassPath classPath; + String classPathStr = System.getProperty("java.class.path") .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; int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;; + int outputStyle = TabbedPrintWriter.BRACE_AT_EOL; + int indentSize = 4; GlobalOptions.err.println(GlobalOptions.copyright); @@ -163,6 +248,25 @@ public class Main extends Options { errorInParams |= !GlobalOptions.setDebugging(arg); 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': { String arg = g.getOptarg(); int comma = arg.indexOf(','); @@ -208,7 +312,8 @@ public class Main extends Options { ZipOutputStream destZip = null; TabbedPrintWriter writer = 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") || destDir.toLowerCase().endsWith(".jar")) { try { @@ -219,61 +324,37 @@ public class Main extends Options { return; } writer = new TabbedPrintWriter(new BufferedOutputStream(destZip), - imports, false); + imports, false, + outputStyle, indentSize, 0, 79); } for (int i= g.getOptind(); i< params.length; i++) { try { - ClassInfo clazz; - try { - clazz = classPath.getClassInfo(params[i]); - } catch (IllegalArgumentException ex) { - GlobalOptions.err.println - ("`"+params[i]+"' is not a class name"); - continue; - } - if (skipClass(clazz)) - continue; - - String filename = - params[i].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."); + if ((params[i].endsWith(".jar") || params[i].endsWith(".zip")) + && new File(params[i]).isFile()) { + /* The user obviously wants to decompile a jar/zip file. + * Lets do him a pleasure and allow this. + */ + ClassPath zipClassPath + = new ClassPath(params[i], classPath); + Enumeration enum = new ZipFile(params[i]).entries(); + while (enum.hasMoreElements()) { + String entry + = ((ZipEntry) enum.nextElement()).getName(); + if (entry.endsWith(".class")) { + entry = entry.substring(0, entry.length() - 6) + .replace('/', '.'); + decompileClass(entry, zipClassPath, classPathStr, + destZip, destDir, + writer, imports); + } } - writer = new TabbedPrintWriter - (new BufferedOutputStream(new FileOutputStream(file)), - imports, false); - } - - 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."); + } else + decompileClass(params[i], classPath, classPathStr, + destZip, destDir, + writer, imports); } catch (IOException ex) { GlobalOptions.err.println - ("Can't write source of "+params[i]+"."); - GlobalOptions.err.println("Check the permissions."); + ("Can't read zip file " + params[i] + "."); ex.printStackTrace(GlobalOptions.err); } } diff --git a/jode/jode/decompiler/MethodAnalyzer.java b/jode/jode/decompiler/MethodAnalyzer.java index b37680e..3f7e6de 100644 --- a/jode/jode/decompiler/MethodAnalyzer.java +++ b/jode/jode/decompiler/MethodAnalyzer.java @@ -833,6 +833,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { writer.print(" " + methodName); } writer.breakOp(); + writer.printOptionalSpace(); writer.print("("); writer.startOp(writer.EXPL_PAREN, 0); int offset = skipParams + (isStatic() ? 0 : 1); @@ -862,11 +863,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { } writer.endOp(); if (bb != null) { - writer.openBrace(); + writer.openBraceNoIndent(); writer.tab(); methodHeader.dumpSource(writer); writer.untab(); - writer.closeBrace(); + writer.closeBraceNoIndent(); } else writer.println(";"); writer.popScope(); @@ -975,8 +976,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { expr).getSubExpressions()[0]; if (expr instanceof ThisOperator) { outerValueArray[j] = - new ThisOperator(((ThisOperator) - expr).getClassInfo()); + new ThisOperator(((ThisOperator) expr).getClassInfo()); continue; } LocalInfo li = null; diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java index ea9da72..1e4652a 100644 --- a/jode/jode/decompiler/TabbedPrintWriter.java +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -40,7 +40,9 @@ public class TabbedPrintWriter { private ImportHandler imports; 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 @@ -132,6 +134,7 @@ public class TabbedPrintWriter { * our child, if possible. */ BreakPoint child = (BreakPoint) childBPs.elementAt(0); + options = child.options; startPos = child.startPos; options = child.options; endPos = child.endPos; @@ -727,6 +730,11 @@ public class TabbedPrintWriter { else return type.toString(); } + + public void printOptionalSpace() { + if ((style & GNU_SPACING) != 0) + print(" "); + } /** * Print a opening brace with the current indentation style. @@ -734,63 +742,80 @@ public class TabbedPrintWriter { * brace. It doesn't do a tab stop after opening the brace. */ public void openBrace() { - if ((style & BRACE_AT_EOL) != 0) { - print(currentLine.length() > 0 ? " {" : "{"); + boolean bracePrinted = false; + if (currentLine.length() > 0) { + if ((style & BRACE_AT_EOL) != 0) { + print(" {"); + bracePrinted = true; + } 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. + * 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 * brace. It doesn't do a tab stop after opening the brace. */ public void openBraceNoSpace() { - if ((style & BRACE_AT_EOL) != 0) - println("{"); - else { - if (currentLine.length() > 0) - println(); - if (currentIndent > 0) - tab(); - println("{"); + boolean bracePrinted = false; + if (currentLine.length() > 0) { + if ((style & BRACE_AT_EOL) != 0) { + print("{"); + bracePrinted = true; + } + println(); } + if ((style & INDENT_BRACES) != 0 && currentIndent > 0) + tab(); + if (!bracePrinted) + println("{"); } public void closeBraceContinue() { if ((style & BRACE_AT_EOL) != 0) print("} "); - else { + else println("}"); - if (currentIndent > 0) - untab(); - } + if ((style & INDENT_BRACES) != 0 && currentIndent > 0) + untab(); } - public void closeBraceNoSpace() { - if ((style & BRACE_AT_EOL) != 0) - print("}"); - else { - println("}"); - if (currentIndent > 0) - untab(); - } + public void closeBraceClass() { + print("}"); } public void closeBrace() { - if ((style & BRACE_AT_EOL) != 0) - println("}"); - else { - println("}"); - if (currentIndent > 0) - untab(); - } + println("}"); + if ((style & INDENT_BRACES) != 0 && currentIndent > 0) + untab(); + } + + public void closeBraceNoIndent() { + println("}"); } public void flush() { diff --git a/jode/jode/expr/Expression.java b/jode/jode/expr/Expression.java index 7dd15a7..84c6939 100644 --- a/jode/jode/expr/Expression.java +++ b/jode/jode/expr/Expression.java @@ -55,23 +55,7 @@ public abstract class Expression { } public void updateParentType(Type otherType) { - Type newType = otherType.intersection(type); - 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; + setType(otherType); if (parent != null) parent.updateType(); } diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java index bfb1cd2..3a66b77 100644 --- a/jode/jode/expr/IfThenElseOperator.java +++ b/jode/jode/expr/IfThenElseOperator.java @@ -40,9 +40,9 @@ public class IfThenElseOperator extends Operator { } public void updateType() { - Type subType = Type.tSuperType(subExpressions[1].getType()) + Type commonType = Type.tSuperType(subExpressions[1].getType()) .intersection(Type.tSuperType(subExpressions[2].getType())); - updateParentType(subType); + updateParentType(commonType); } public Expression simplify() { diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java index fe52e3f..e5f6f47 100644 --- a/jode/jode/expr/InvokeOperator.java +++ b/jode/jode/expr/InvokeOperator.java @@ -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() { 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. */ public boolean isGetClass() { - if (isThis()) { - SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); - if (synth != null && synth.getKind() == SyntheticAnalyzer.GETCLASS) - return true; - } - return false; + MethodAnalyzer mana = getMethodAnalyzer(); + if (mana == null) + return false; + SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); + return (synth != null + && synth.getKind() == SyntheticAnalyzer.GETCLASS); } class Environment extends SimpleRuntimeEnvironment { @@ -712,9 +712,10 @@ public final class InvokeOperator extends Operator } for (int p = offset; p < paramTypes.length; p++) { if (!paramTypes[p] - .isOfType(Type.tSubType(otherParamTypes[p-offset]))) + .isOfType(Type.tSubType(otherParamTypes[p-offset]))){ /* No conflict here */ continue next_method; + } } /* There is a conflict that can be resolved by a cast. */ return true; @@ -1100,30 +1101,19 @@ public final class InvokeOperator extends Operator ThisOperator thisOp = (ThisOperator) subExpressions[0]; Scope scope = writer.getScope(thisOp.getClassInfo(), 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); writer.breakOp(); 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 { if (needsCast(0, paramTypes)){ @@ -1150,6 +1140,7 @@ public final class InvokeOperator extends Operator * We still need to check for casts though. */ writer.breakOp(); + writer.printOptionalSpace(); writer.print("("); writer.startOp(writer.EXPL_PAREN, 0); boolean first = true; @@ -1183,11 +1174,11 @@ public final class InvokeOperator extends Operator */ if (anonymousNew) { Object state = writer.saveOps(); - writer.openBrace(); + writer.openBraceClass(); writer.tab(); clazzAna.dumpBlock(writer); writer.untab(); - writer.closeBraceNoSpace(); + writer.closeBraceClass(); writer.restoreOps(state); } } diff --git a/jode/jode/expr/PopOperator.java b/jode/jode/expr/PopOperator.java index 4fa839c..89b59c7 100644 --- a/jode/jode/expr/PopOperator.java +++ b/jode/jode/expr/PopOperator.java @@ -41,8 +41,17 @@ public class PopOperator extends Operator { public void updateType() { } + public int getBreakPenalty() { + if (subExpressions[0] instanceof Operator) + return ((Operator) subExpressions[0]).getBreakPenalty(); + return 0; + } + public void dumpExpression(TabbedPrintWriter writer) 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); } } diff --git a/jode/jode/expr/UnaryOperator.java b/jode/jode/expr/UnaryOperator.java index 1722442..2fce590 100644 --- a/jode/jode/expr/UnaryOperator.java +++ b/jode/jode/expr/UnaryOperator.java @@ -57,6 +57,7 @@ public class UnaryOperator extends Operator { public void dumpExpression(TabbedPrintWriter writer) throws java.io.IOException { writer.print(getOperatorString()); + writer.printOptionalSpace(); subExpressions[0].dumpExpression(writer, 700); } } diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java index d2b961c..caf6441 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -772,9 +772,6 @@ public class TransformExceptionHandlers { if (subRoutine != null) { 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 * catchFlow as predecessor. diff --git a/jode/jode/jvm/SyntheticAnalyzer.java b/jode/jode/jvm/SyntheticAnalyzer.java index a4f7e80..3cae53c 100644 --- a/jode/jode/jvm/SyntheticAnalyzer.java +++ b/jode/jode/jvm/SyntheticAnalyzer.java @@ -52,7 +52,7 @@ public class SyntheticAnalyzer implements Opcodes { int kind = UNKNOWN; - int unifyParam; + int unifyParam = -1; Reference reference; ClassInfo classInfo; MethodInfo method; @@ -334,7 +334,7 @@ public class SyntheticAnalyzer implements Opcodes { params++; slot++; } - if (instr.getOpcode() == opc_invokespecial) { + if (params > 0 && instr.getOpcode() == opc_invokespecial) { Reference ref = instr.getReference(); String refClazz = ref.getClazz().substring(1); if (!(refClazz.substring(0, refClazz.length()-1) diff --git a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java index ebf7893..6e8dedd 100644 --- a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java +++ b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java @@ -231,7 +231,7 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { if (fi != null && !fi.isNotConstant()) { Object result = fi.getConstant(); if (result == null) - result = getDefaultValue(ref.getType()); + result = TypeSignature.getDefaultValue(ref.getType()); return result; } throw new InterpreterException("Field " + ref + " not constant"); diff --git a/jode/jode/obfuscator/modules/ConstantAnalyzer.java b/jode/jode/obfuscator/modules/ConstantAnalyzer.java index 60adb34..8ee0444 100644 --- a/jode/jode/obfuscator/modules/ConstantAnalyzer.java +++ b/jode/jode/obfuscator/modules/ConstantAnalyzer.java @@ -1839,8 +1839,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Handler[] newHandlers = new Handler[newHandlerCtr]; System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr); System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr); - bb.setBlocks(newBlocks, newStartBlock); - bb.setExceptionHandlers(newHandlers); + bb.setBlocks(newBlocks, newStartBlock, newHandlers); } } } diff --git a/jode/jode/swingui/Main.java b/jode/jode/swingui/Main.java index d3c0206..e7e1376 100644 --- a/jode/jode/swingui/Main.java +++ b/jode/jode/swingui/Main.java @@ -104,9 +104,11 @@ public class Main classTree.addTreeSelectionListener(this); JScrollPane spClassTree = new JScrollPane(classTree); sourcecodeArea = new JTextArea(20, 80); + sourcecodeArea.setEditable(false); sourcecodeArea.setFont(monospaced); JScrollPane spText = new JScrollPane(sourcecodeArea); errorArea = new JTextArea(3, 80); + errorArea.setEditable(false); errorArea.setFont(monospaced); JScrollPane spError = new JScrollPane(errorArea); diff --git a/jode/jode/type/NullType.java b/jode/jode/type/NullType.java index c5422b3..128b881 100644 --- a/jode/jode/type/NullType.java +++ b/jode/jode/type/NullType.java @@ -83,15 +83,4 @@ public class NullType extends ReferenceType { public Type findCommonClassTypes(Stack otherTypes) { 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; - } }