diff --git a/jode/jode/decompiler/Analyzer.java b/jode/jode/decompiler/Analyzer.java index 41dd019..2628d02 100644 --- a/jode/jode/decompiler/Analyzer.java +++ b/jode/jode/decompiler/Analyzer.java @@ -22,6 +22,5 @@ package jode.decompiler; public interface Analyzer { public void analyze(); - public void makeDeclaration(); public void dumpSource(TabbedPrintWriter writer) throws java.io.IOException; } diff --git a/jode/jode/decompiler/Applet.java b/jode/jode/decompiler/Applet.java new file mode 100644 index 0000000..545d6ae --- /dev/null +++ b/jode/jode/decompiler/Applet.java @@ -0,0 +1,41 @@ +/* Applet 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; + +public class Applet extends java.applet.Applet { + Window jodeWin = new Window(this); + +///#ifdef AWT10 +/// public boolean action(Event e, Object arg) { +/// jodeWin.action(e, arg); +/// return true; +/// } +///#endif + + public void init() { + String cp = getParameter("classpath"); + if (cp != null) + jodeWin.setClasspath(cp); + String cls = getParameter("class"); + if (cls != null) + jodeWin.setClass(cls); + } +} + diff --git a/jode/jode/decompiler/ClassAnalyzer.java.in b/jode/jode/decompiler/ClassAnalyzer.java.in index 4a5fb4d..f83c730 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java.in +++ b/jode/jode/decompiler/ClassAnalyzer.java.in @@ -19,7 +19,6 @@ package jode.decompiler; import jode.GlobalOptions; -import jode.Decompiler; import jode.type.MethodType; import jode.type.Type; import jode.bytecode.ClassInfo; @@ -32,6 +31,7 @@ import jode.expr.ThisOperator; import jode.flow.TransformConstructors; import jode.flow.StructuredBlock; import jode.flow.FlowBlock; +import jode.flow.VariableSet; import java.lang.reflect.Modifier; import java.util.NoSuchElementException; @@ -40,6 +40,7 @@ import java.util.Enumeration; import java.io.IOException; import @COLLECTIONS@.Collection; +import @COLLECTIONS@.Set; public class ClassAnalyzer implements Analyzer, Scope, Declarable, ClassDeclarer @@ -184,7 +185,7 @@ public class ClassAnalyzer return; } - if ((Decompiler.options & Decompiler.OPTION_INNER) != 0 + if ((Options.options & Options.OPTION_INNER) != 0 && innerInfos != null) { /* Create inner classes */ Expression[] outerThis = new Expression[] { @@ -263,7 +264,7 @@ public class ClassAnalyzer // If output should be immediate, we delay analyzation to output. // Note that this may break anonymous classes, but the user // has been warned. - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) return; // Analyze fields @@ -281,7 +282,7 @@ public class ClassAnalyzer // If output should be immediate, we delay analyzation to output. // Note that this may break anonymous classes, but the user // has been warned. - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) return; // Now analyze the inner classes. @@ -296,7 +297,7 @@ public class ClassAnalyzer } - public void makeDeclaration() { + public void makeDeclaration(Set done) { if (constrAna != null) constrAna.transform(); if (staticConstructor != null) { @@ -308,16 +309,17 @@ public class ClassAnalyzer // If output should be immediate, we delay analyzation to output. // Note that this may break anonymous classes, but the user // has been warned. - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) return; for (int j=0; j < fields.length; j++) - fields[j].makeDeclaration(); + fields[j].makeDeclaration(done); for (int j=0; j < inners.length; j++) - inners[j].makeDeclaration(); + inners[j].makeDeclaration(done); for (int j=0; j < methods.length; j++) - methods[j].makeDeclaration(); + methods[j].makeDeclaration(done); } + public void dumpDeclaration(TabbedPrintWriter writer) throws IOException { dumpSource(writer); @@ -328,6 +330,9 @@ public class ClassAnalyzer writer.pushScope(this); boolean needFieldNewLine = false; boolean needNewLine = false; + Set declared = null; + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) + declared = new VariableSet(); for (int i=0; i< fields.length; i++) { if (blockInitializers[i] != null) { if (needNewLine) @@ -339,10 +344,10 @@ public class ClassAnalyzer writer.closeBrace(); needFieldNewLine = needNewLine = true; } - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) { + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { // We now do the analyzation we skipped before. fields[i].analyze(); - fields[i].makeDeclaration(); + fields[i].makeDeclaration(declared); } if (fields[i].skipWriting()) continue; @@ -365,23 +370,23 @@ public class ClassAnalyzer if (needNewLine) writer.println(""); - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) { + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { // We now do the analyzation we skipped before. inners[i].analyze(); inners[i].analyzeInnerClasses(); - inners[i].makeDeclaration(); + inners[i].makeDeclaration(declared); } inners[i].dumpSource(writer); needNewLine = true; } for (int i=0; i< methods.length; i++) { - if ((Decompiler.options & Decompiler.OPTION_IMMEDIATE) != 0) { + if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { // We now do the analyzation we skipped before. if (!methods[i].isConstructor()) methods[i].analyze(); methods[i].analyzeInnerClasses(); - methods[i].makeDeclaration(); + methods[i].makeDeclaration(declared); } if (methods[i].skipWriting()) @@ -451,6 +456,7 @@ public class ClassAnalyzer writer.closeBraceNoSpace(); } else writer.closeBrace(); + clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS); } public void dumpJavaFile(TabbedPrintWriter writer) throws IOException { @@ -459,7 +465,7 @@ public class ClassAnalyzer initialize(); analyze(); analyzeInnerClasses(); - makeDeclaration(); + makeDeclaration(new VariableSet()); imports.dumpHeader(writer); dumpSource(writer); diff --git a/jode/jode/decompiler/FieldAnalyzer.java b/jode/jode/decompiler/FieldAnalyzer.java.in similarity index 97% rename from jode/jode/decompiler/FieldAnalyzer.java rename to jode/jode/decompiler/FieldAnalyzer.java.in index ba4d446..b4131e1 100644 --- a/jode/jode/decompiler/FieldAnalyzer.java +++ b/jode/jode/decompiler/FieldAnalyzer.java.in @@ -29,6 +29,8 @@ import jode.expr.OuterLocalOperator; import java.lang.reflect.Modifier; import java.io.IOException; +import @COLLECTIONS@.Set; + public class FieldAnalyzer implements Analyzer { ClassAnalyzer clazz; ImportHandler imports; @@ -137,9 +139,11 @@ public class FieldAnalyzer implements Analyzer { imports.useType(type); } - public void makeDeclaration() { - if (constant != null) + public void makeDeclaration(Set done) { + if (constant != null) { + constant.makeDeclaration(done); constant = constant.simplify(); + } } public boolean skipWriting() { diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java index f9094c1..261a548 100644 --- a/jode/jode/decompiler/LocalInfo.java +++ b/jode/jode/decompiler/LocalInfo.java @@ -21,7 +21,6 @@ package jode.decompiler; import java.util.Enumeration; import java.util.Vector; import jode.GlobalOptions; -import jode.Decompiler; import jode.type.Type; import jode.expr.Expression; import jode.expr.LocalVarOperator; @@ -221,7 +220,7 @@ public class LocalInfo implements Declarable { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) GlobalOptions.err.println(getName()+" set type to getHint()"); setType(type.getHint()); - if ((Decompiler.options & Decompiler.OPTION_PRETTY) != 0) { + if ((Options.options & Options.OPTION_PRETTY) != 0) { name = type.getDefaultName(); } else { name = type.getDefaultName() diff --git a/jode/jode/decompiler/LocalVariableTable.java b/jode/jode/decompiler/LocalVariableTable.java index 9492f01..162c5e4 100644 --- a/jode/jode/decompiler/LocalVariableTable.java +++ b/jode/jode/decompiler/LocalVariableTable.java @@ -18,8 +18,6 @@ */ package jode.decompiler; -import java.io.*; -import jode.Decompiler; import jode.type.Type; import jode.bytecode.LocalVariableInfo; diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java new file mode 100644 index 0000000..8b84c48 --- /dev/null +++ b/jode/jode/decompiler/Main.java @@ -0,0 +1,328 @@ +/* Main Copyright (C) 1998-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.ClassInfo; +import jode.bytecode.SearchPath; +import jode.GlobalOptions; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; + +import gnu.getopt.LongOpt; +import gnu.getopt.Getopt; + + +public class Main extends Options { + private static final int OPTION_START=0x10000; + private static final int OPTION_END =0x20000; + + private static final LongOpt[] longOptions = new LongOpt[] { + new LongOpt("cp", LongOpt.REQUIRED_ARGUMENT, null, 'c'), + new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT, null, 'c'), + new LongOpt("dest", LongOpt.REQUIRED_ARGUMENT, null, 'd'), + new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'), + new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'), + 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, + OPTION_START+1), + new LongOpt("anonymous", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+2), + new LongOpt("push", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+3), + new LongOpt("pretty", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+4), + new LongOpt("decrypt", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+5), + new LongOpt("onetime", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+6), + new LongOpt("immediate", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+7), + new LongOpt("verify", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+8), + new LongOpt("contrafo", LongOpt.OPTIONAL_ARGUMENT, null, + OPTION_START+9) + }; + + public static void usage() { + PrintWriter err = GlobalOptions.err; + err.println("Version: " + GlobalOptions.version); + err.println("Usage: java jode.decompiler.Main [OPTIONS]... [CLASSES]..."); + err.println(" -h, --help "+ + "show this information."); + err.println(" -V, --version "+ + "output version information and exit."); + err.println(" -v, --verbose "+ + "be verbose (multiple times means more verbose)."); + err.println(" -c, --classpath "+ + "search for classes in specified classpath."); + err.println(" "+ + "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."); + + err.println("The following options can be turned on or off with `yes' or `no' argument."); + err.println(" --inner "+ + "decompile inner classes (default)."); + err.println(" --anonymous "+ + "decompile anonymous classes (default)."); + err.println(" --contrafo "+ + "transform constructors of inner classes (default)."); + err.println(" --lvt "+ + "use the local variable table (default)."); + err.println(" --pretty "+ + "use `pretty' names for local variables."); + err.println(" --push "+ + "allow PUSH instructions in output."); + err.println(" --decrypt "+ + "decrypt encrypted strings (default)."); + err.println(" --onetime "+ + "remove locals, that are used only one time."); + err.println(" --immediate "+ + "output source immediately (may produce buggy code)."); + err.println(" --verify "+ + "verify code before decompiling it."); + err.println("Debugging options, mainly used to debug this decompiler:"); + err.println(" -D, --debug=... "+ + "use --debug=help for more information."); + } + + public static boolean handleOption(int option, int longind, String arg) { + if (arg == null) + options ^= 1 << option; + else if ("yes".startsWith(arg) || arg.equals("on")) + options |= 1 << option; + else if ("no".startsWith(arg) || arg.equals("off")) + options &= ~(1 << option); + else { + GlobalOptions.err.println + ("jode.decompiler.Main: option --"+longOptions[longind].getName() + +" takes one of `yes', `no', `on', `off' as parameter"); + return false; + } + return true; + } + + public static void main(String[] params) { + if (params.length == 0) { + usage(); + return; + } + + String classPath = System.getProperty("java.class.path") + .replace(File.pathSeparatorChar, SearchPath.altPathSeparatorChar); + String destDir = null; + + int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; + int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;; + + GlobalOptions.err.println(GlobalOptions.copyright); + + boolean errorInParams = false; + Getopt g = new Getopt("jode.decompiler.Main", params, "hVvc:d:D:i:s:", + longOptions, true); + for (int opt = g.getopt(); opt != -1; opt = g.getopt()) { + switch(opt) { + case 0: + break; + case 'h': + usage(); + errorInParams = true; + break; + case 'V': + GlobalOptions.err.println(GlobalOptions.version); + break; + case 'c': + classPath = g.getOptarg(); + break; + case 'd': + destDir = g.getOptarg(); + break; + case 'v': { + String arg = g.getOptarg(); + if (arg == null) + GlobalOptions.verboseLevel++; + else { + try { + GlobalOptions.verboseLevel = Integer.parseInt(arg); + } catch (NumberFormatException ex) { + GlobalOptions.err.println + ("jode.decompiler.Main: Argument `" + +arg+"' to --verbose must be numeric:"); + errorInParams = true; + } + } + break; + } + case 'D': { + String arg = g.getOptarg(); + if (arg == null) + arg = "help"; + errorInParams |= !GlobalOptions.setDebugging(arg); + break; + } + case 's': { + String arg = g.getOptarg(); + if ("sun".startsWith(arg)) + outputStyle = SUN_STYLE; + else if ("gnu".startsWith(arg)) + outputStyle = GNU_STYLE; + else { + GlobalOptions.err.println + ("jode.decompiler.Main: Unknown style `"+arg+"'."); + errorInParams = true; + } + break; + } + case 'i': { + String arg = g.getOptarg(); + int comma = arg.indexOf(','); + try { + int packLimit = Integer.parseInt(arg.substring(0, comma)); + if (packLimit == 0) + packLimit = Integer.MAX_VALUE; + if (packLimit < 0) + throw new IllegalArgumentException(); + int clazzLimit = Integer.parseInt(arg.substring(comma+1)); + if (clazzLimit == 0) + clazzLimit = Integer.MAX_VALUE; + if (clazzLimit < 0) + throw new IllegalArgumentException(); + + importPackageLimit = packLimit; + importClassLimit = clazzLimit; + + } catch (RuntimeException ex) { + GlobalOptions.err.println + ("jode.decompiler.Main: Invalid argument for -i option."); + errorInParams = true; + } + break; + } + default: + if (opt >= OPTION_START && opt <= OPTION_END) { + errorInParams |= !handleOption(opt-OPTION_START, + g.getLongind(), + g.getOptarg()); + } else + errorInParams = true; + break; + } + } + if (errorInParams) + return; + ClassInfo.setClassPath(classPath.toString()); + ImportHandler imports = new ImportHandler(importPackageLimit, + importClassLimit); + + ZipOutputStream destZip = null; + TabbedPrintWriter writer = null; + if (destDir == null) + writer = new TabbedPrintWriter(System.out, imports); + else if (destDir.toLowerCase().endsWith(".zip") + || destDir.toLowerCase().endsWith(".jar")) { + try { + destZip = new ZipOutputStream(new FileOutputStream(destDir)); + } catch (IOException ex) { + GlobalOptions.err.println("Can't open zip file "+destDir); + ex.printStackTrace(GlobalOptions.err); + return; + } + writer = new TabbedPrintWriter(new BufferedOutputStream(destZip), + imports, false); + } + for (int i= g.getOptind(); i< params.length; i++) { + try { + ClassInfo clazz; + try { + clazz = ClassInfo.forName(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."); + } + 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 (IOException ex) { + GlobalOptions.err.println + ("Can't write source of "+params[i]+"."); + GlobalOptions.err.println("Check the permissions."); + ex.printStackTrace(GlobalOptions.err); + } + } + if (destZip != null) { + try { + destZip.close(); + } catch (IOException ex) { + GlobalOptions.err.println("Can't close Zipfile"); + ex.printStackTrace(GlobalOptions.err); + } + } + } +} diff --git a/jode/jode/decompiler/Makefile.am b/jode/jode/decompiler/Makefile.am index 5b1b614..be2ae92 100644 --- a/jode/jode/decompiler/Makefile.am +++ b/jode/jode/decompiler/Makefile.am @@ -12,6 +12,7 @@ BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB) MY_JAVA_FILES = \ Analyzer.java \ + Applet.java \ ClassAnalyzer.java \ ClassDeclarer.java \ DeadCodeAnalysis.java \ @@ -22,12 +23,15 @@ MY_JAVA_FILES = \ LocalVarEntry.java \ LocalVariableRangeList.java \ LocalVariableTable.java \ + Main.java \ MethodAnalyzer.java \ Opcodes.java \ + Options.java \ OuterValueListener.java \ OuterValues.java \ Scope.java \ - TabbedPrintWriter.java + TabbedPrintWriter.java \ + Window.java noinst_DATA = $(MY_JAVA_FILES:.java=.class) EXTRA_DIST = $(MY_JAVA_FILES) diff --git a/jode/jode/decompiler/MethodAnalyzer.java.in b/jode/jode/decompiler/MethodAnalyzer.java.in index fa385a4..b13a8ca 100644 --- a/jode/jode/decompiler/MethodAnalyzer.java.in +++ b/jode/jode/decompiler/MethodAnalyzer.java.in @@ -19,7 +19,6 @@ package jode.decompiler; import jode.AssertError; -import jode.Decompiler; import jode.GlobalOptions; import jode.bytecode.BytecodeInfo; import jode.bytecode.ClassInfo; @@ -57,6 +56,7 @@ import @COLLECTIONS@.Map; import @COLLECTIONS@.Collection; import @COLLECTIONS@.ArrayList; import @COLLECTIONS@.Iterator; +import @COLLECTIONS@.Set; /** * A method analyzer is the main class for analyzation of methods. @@ -75,7 +75,7 @@ import @COLLECTIONS@.Iterator; *
This will determine when to declare variables. For constructors * it will do special transformations like field initialization.
*/ -public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { +public class MethodAnalyzer implements Scope, ClassDeclarer { /** * The import handler where we should register our types. */ @@ -200,7 +200,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { if (minfo.getBytecode() != null) { code = minfo.getBytecode(); - if ((Decompiler.options & Decompiler.OPTION_VERIFY) != 0) { + if ((Options.options & Options.OPTION_VERIFY) != 0) { CodeVerifier verifier = new CodeVerifier(getClazz(), minfo, code); try { @@ -211,7 +211,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { } } - if ((Decompiler.options & Decompiler.OPTION_LVT) != 0) { + if ((Options.options & Options.OPTION_LVT) != 0) { LocalVariableInfo[] localvars = code.getLocalVariableTable(); if (localvars != null) lvt = new LocalVariableTable(code.getMaxLocals(), @@ -552,10 +552,10 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { excHandlers.analyze(); methodHeader.analyze(); - if ((Decompiler.options & Decompiler.OPTION_PUSH) == 0 + if ((Options.options & Options.OPTION_PUSH) == 0 && methodHeader.mapStackToLocal()) methodHeader.removePush(); - if ((Decompiler.options & Decompiler.OPTION_ONETIME) != 0) + if ((Options.options & Options.OPTION_ONETIME) != 0) methodHeader.removeOnetimeLocals(); methodHeader.mergeParams(param); @@ -621,29 +621,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { * the types and names of the local variables and where to declare them. * It will also determine where to declare method scoped local variables. */ - public void makeDeclaration() { - for (Enumeration enum = allLocals.elements(); - enum.hasMoreElements(); ) { - LocalInfo li = (LocalInfo)enum.nextElement(); - if (!li.isShadow()) - imports.useType(li.getType()); - } - for (int i=0; i < param.length; i++) { - param[i].guessName(); - for (int j=0; j < i; j++) { - if (param[j].getName().equals(param[i].getName())) { - /* A name conflict happened. */ - param[i].makeNameUnique(); - break; /* j */ - } - } - } - - if (code != null) { - methodHeader.makeDeclaration(param); - methodHeader.simplify(); - } - + public void makeDeclaration(Set done) { if (innerAnalyzers != null) { for (Enumeration enum = innerAnalyzers.elements(); enum.hasMoreElements(); ) { @@ -659,10 +637,38 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { li.markFinal(); } } - classAna.makeDeclaration(); } } } + + for (Enumeration enum = allLocals.elements(); + enum.hasMoreElements(); ) { + LocalInfo li = (LocalInfo)enum.nextElement(); + if (!li.isShadow()) + imports.useType(li.getType()); + } + for (int i=0; i < param.length; i++) { + param[i].guessName(); + Iterator doneIter = done.iterator(); + while (doneIter.hasNext()) { + Declarable previous = (Declarable) doneIter.next(); + if (param[i].getName().equals(previous.getName())) { + /* A name conflict happened. */ + param[i].makeNameUnique(); + break; + } + } + done.add(param[i]); + } + + if (code != null) { + methodHeader.makeDeclaration(done); + methodHeader.simplify(); + } + for (int i=0; i < param.length; i++) { + done.remove(param[i]); + // remove the parameters, since we leave the scope + } } /** @@ -677,8 +683,8 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { return true; if (synth.getKind() >= synth.ACCESSGETFIELD && synth.getKind() <= synth.ACCESSCONSTRUCTOR - && (Decompiler.options & Decompiler.OPTION_INNER) != 0 - && (Decompiler.options & Decompiler.OPTION_ANON) != 0) + && (Options.options & Options.OPTION_INNER) != 0 + && (Options.options & Options.OPTION_ANON) != 0) return true; } @@ -750,7 +756,7 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { int modifiedModifiers = minfo.getModifiers(); if (isConstructor() && !isStatic() - && (Decompiler.options & Decompiler.OPTION_CONTRAFO) != 0 + && (Options.options & Options.OPTION_CONTRAFO) != 0 && classAnalyzer.outerValues != null) skipParams = classAnalyzer.outerValues.getCount(); @@ -771,11 +777,6 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { writer.pushScope(this); - if (minfo.isSynthetic() - && (classAnalyzer.getName() != null - || !isConstructor())) - writer.print("/*synthetic*/ "); - /* * JLS-1.0, section 9.4: * @@ -793,15 +794,31 @@ public class MethodAnalyzer implements Analyzer, Scope, ClassDeclarer { */ if (classAnalyzer.getClazz().isInterface()) modifiedModifiers &= ~Modifier.ABSTRACT; + + /* Don't ask me why, but jikes declares the static constructor + * as final. + */ + if (isConstructor() && isStatic()) + modifiedModifiers &= ~Modifier.FINAL; + + + String delim =""; + if (minfo.isSynthetic()) { + writer.print("/*synthetic*/"); + delim = " "; + } + String modif = Modifier.toString(modifiedModifiers); + writer.print(delim + modif); if (modif.length() > 0) - writer.print(modif+" "); + delim = " "; if (isConstructor && (isStatic() || (classAnalyzer.getName() == null && skipParams == methodType.getParameterTypes().length))) { /* static block or unnamed constructor */ } else { + writer.print(delim); if (declareAsConstructor) writer.print(classAnalyzer.getName()); else { diff --git a/jode/jode/decompiler/Options.java b/jode/jode/decompiler/Options.java new file mode 100644 index 0000000..e7f0661 --- /dev/null +++ b/jode/jode/decompiler/Options.java @@ -0,0 +1,66 @@ +/* Options Copyright (C) 1998-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.ClassInfo; +import jode.bytecode.InnerClassInfo; + +public class Options { + public static final int TAB_SIZE_MASK = 0x0f; + public static final int BRACE_AT_EOL = 0x10; + public static final int SUN_STYLE = 0x14; + public static final int GNU_STYLE = 0x02; + + public static final int OPTION_LVT = 0x0001; + public static final int OPTION_INNER = 0x0002; + public static final int OPTION_ANON = 0x0004; + public static final int OPTION_PUSH = 0x0008; + public static final int OPTION_PRETTY = 0x0010; + public static final int OPTION_DECRYPT = 0x0020; + public static final int OPTION_ONETIME = 0x0040; + public static final int OPTION_IMMEDIATE = 0x0080; + public static final int OPTION_VERIFY = 0x0100; + public static final int OPTION_CONTRAFO = 0x0200; + + public static int options = + OPTION_LVT | OPTION_INNER | OPTION_ANON | + OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO; + + public static int outputStyle = SUN_STYLE; + + public final static boolean doAnonymous() { + return (options & OPTION_ANON) != 0; + } + + public final static boolean doInner() { + return (options & OPTION_INNER) != 0; + } + + public static boolean skipClass(ClassInfo clazz) { + InnerClassInfo[] outers = clazz.getOuterClasses(); + if (outers != null) { + if (outers[0].outer == null) { + return doAnonymous(); + } else { + return doInner(); + } + } + return false; + } +} diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java index 81674aa..4ed8acf 100644 --- a/jode/jode/decompiler/TabbedPrintWriter.java +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -20,7 +20,6 @@ package jode.decompiler; import java.io.*; import java.util.Stack; -import jode.Decompiler; import jode.GlobalOptions; import jode.bytecode.ClassInfo; import jode.bytecode.InnerClassInfo; @@ -39,7 +38,7 @@ public class TabbedPrintWriter { boolean autoFlush) { pw = new PrintWriter(os, autoFlush); this.imports = imports; - this.indentsize = (Decompiler.outputStyle & Decompiler.TAB_SIZE_MASK); + this.indentsize = (Options.outputStyle & Options.TAB_SIZE_MASK); atbol = true; } @@ -47,7 +46,7 @@ public class TabbedPrintWriter { boolean autoFlush) { pw = new PrintWriter(os, autoFlush); this.imports = imports; - this.indentsize = (Decompiler.outputStyle & Decompiler.TAB_SIZE_MASK); + this.indentsize = (Options.outputStyle & Options.TAB_SIZE_MASK); atbol = true; } @@ -129,7 +128,7 @@ public class TabbedPrintWriter { * brace. It doesn't do a tab stop after opening the brace. */ public void openBrace() { - if ((Decompiler.outputStyle & Decompiler.BRACE_AT_EOL) != 0) + if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) if (atbol) println("{"); else @@ -247,14 +246,14 @@ public class TabbedPrintWriter { public String getClassString(ClassInfo clazz, int scopeType) { String name = clazz.getName(); if (name.indexOf('$') >= 0) { - if ((Decompiler.options & Decompiler.OPTION_INNER) != 0) { + if ((Options.options & Options.OPTION_INNER) != 0) { String innerClassName = getInnerClassString(clazz, scopeType); if (innerClassName != null) return innerClassName; } - if ((Decompiler.options - & Decompiler.OPTION_ANON) != 0) { + if ((Options.options + & Options.OPTION_ANON) != 0) { String innerClassName = getAnonymousClassString(clazz, scopeType); if (innerClassName != null) @@ -289,7 +288,7 @@ public class TabbedPrintWriter { * brace. It doesn't do a tab stop after opening the brace. */ public void openBraceNoSpace() { - if ((Decompiler.outputStyle & Decompiler.BRACE_AT_EOL) != 0) + if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) println("{"); else { if (!atbol) @@ -301,7 +300,7 @@ public class TabbedPrintWriter { } public void closeBraceContinue() { - if ((Decompiler.outputStyle & Decompiler.BRACE_AT_EOL) != 0) + if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) print("} "); else { println("}"); @@ -311,7 +310,7 @@ public class TabbedPrintWriter { } public void closeBraceNoSpace() { - if ((Decompiler.outputStyle & Decompiler.BRACE_AT_EOL) != 0) + if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) print("}"); else { println("}"); @@ -321,7 +320,7 @@ public class TabbedPrintWriter { } public void closeBrace() { - if ((Decompiler.outputStyle & Decompiler.BRACE_AT_EOL) != 0) + if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) println("}"); else { println("}"); diff --git a/jode/jode/decompiler/Window.java b/jode/jode/decompiler/Window.java new file mode 100644 index 0000000..6002253 --- /dev/null +++ b/jode/jode/decompiler/Window.java @@ -0,0 +1,310 @@ +/* Window 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 java.applet.*; +import java.awt.*; +///#ifndef AWT10 +import java.awt.event.*; +///#endif +import java.io.*; +import jode.bytecode.ClassInfo; +import jode.bytecode.SearchPath; +import jode.GlobalOptions; + +public class Window + implements Runnable +///#ifndef AWT10 + , ActionListener +///#endif + +{ + TextField classpathField, classField; + TextArea sourcecodeArea, errorArea; + Checkbox verboseCheck, prettyCheck; + Button startButton, saveButton; + String lastClassName; + Frame frame; + + Thread decompileThread; + + public Window(Container window) { + buildComponents(window); + } + + private void buildComponents(Container window) { + if (window instanceof Frame) + frame = (Frame) window; + + window.setFont(new Font("dialog", Font.PLAIN, 10)); + + classpathField = new TextField(50); + classField = new TextField(50); + sourcecodeArea = new TextArea(20, 80); + errorArea = new TextArea(3, 80); + verboseCheck = new Checkbox("verbose", false); + prettyCheck = new Checkbox("pretty", false); + startButton = new Button("start"); + saveButton = new Button("save"); +///#ifdef AWT10 +/// saveButton.disable(); +///#else + saveButton.setEnabled(false); +///#endif + + sourcecodeArea.setEditable(false); + errorArea.setEditable(false); + Font monospaced = new Font("monospaced", Font.PLAIN, 10); + sourcecodeArea.setFont(monospaced); + errorArea.setFont(monospaced); + + GridBagLayout gbl = new GridBagLayout(); + window.setLayout(gbl); + GridBagConstraints labelConstr = new GridBagConstraints(); + GridBagConstraints textConstr = new GridBagConstraints(); + GridBagConstraints areaConstr = new GridBagConstraints(); + GridBagConstraints checkConstr = new GridBagConstraints(); + GridBagConstraints buttonConstr = new GridBagConstraints(); + labelConstr.fill = GridBagConstraints.NONE; + textConstr.fill = GridBagConstraints.HORIZONTAL; + areaConstr.fill = GridBagConstraints.BOTH; + checkConstr.fill = GridBagConstraints.NONE; + buttonConstr.fill = GridBagConstraints.NONE; + labelConstr.anchor = GridBagConstraints.EAST; + textConstr.anchor = GridBagConstraints.CENTER; + checkConstr.anchor = GridBagConstraints.WEST; + buttonConstr.anchor = GridBagConstraints.CENTER; + labelConstr.anchor = GridBagConstraints.EAST; + textConstr.gridwidth = GridBagConstraints.REMAINDER; + textConstr.weightx = 1.0; + areaConstr.gridwidth = GridBagConstraints.REMAINDER; + areaConstr.weightx = 1.0; + areaConstr.weighty = 1.0; + +///#ifdef AWT10 +/// Label label = new Label("class path: "); +/// gbl.setConstraints(label, labelConstr); +/// window.add(label); +/// gbl.setConstraints(classpathField, textConstr); +/// window.add(classpathField); +/// label = new Label("class name: "); +/// gbl.setConstraints(label, labelConstr); +/// window.add(label); +/// gbl.setConstraints(classField, textConstr); +/// window.add(classField); +/// gbl.setConstraints(verboseCheck, checkConstr); +/// window.add(verboseCheck); +/// gbl.setConstraints(prettyCheck, checkConstr); +/// window.add(prettyCheck); +/// labelConstr.weightx = 1.0; +/// label = new Label(); +/// gbl.setConstraints(label, labelConstr); +/// window.add(label); +/// gbl.setConstraints(startButton, buttonConstr); +/// window.add(startButton); +/// buttonConstr.gridwidth = GridBagConstraints.REMAINDER; +/// gbl.setConstraints(saveButton, buttonConstr); +/// window.add(saveButton); +/// gbl.setConstraints(sourcecodeArea, areaConstr); +/// window.add(sourcecodeArea); +/// areaConstr.gridheight = GridBagConstraints.REMAINDER; +/// areaConstr.weighty = 0.0; +/// gbl.setConstraints(errorArea, areaConstr); +/// window.add(errorArea); +///#else + window.add(new Label("class path: "), labelConstr); + window.add(classpathField, textConstr); + window.add(new Label("class name: "), labelConstr); + window.add(classField, textConstr); + window.add(verboseCheck, checkConstr); + window.add(prettyCheck, checkConstr); + labelConstr.weightx = 1.0; + window.add(new Label(), labelConstr); + window.add(startButton, buttonConstr); + buttonConstr.gridwidth = GridBagConstraints.REMAINDER; + window.add(saveButton, buttonConstr); + window.add(sourcecodeArea, areaConstr); + areaConstr.gridheight = GridBagConstraints.REMAINDER; + areaConstr.weighty = 0.0; + window.add(errorArea, areaConstr); + + startButton.addActionListener(this); + saveButton.addActionListener(this); +///#endif + GlobalOptions.err = new PrintWriter(new AreaWriter(errorArea)); + } + + public void setClasspath(String cp) { + classpathField.setText(cp); + } + public void setClass(String cls) { + classField.setText(cls); + } + +///#ifdef AWT10 +/// public synchronized void action(Event e, Object target) { +///#else + public synchronized void actionPerformed(ActionEvent e) { + Object target = e.getSource(); +///#endif + if (target == startButton) { + +///#ifdef AWT10 +/// startButton.disable(); +///#else + startButton.setEnabled(false); +///#endif + decompileThread = new Thread(this); + sourcecodeArea.setText("Please wait, while decompiling...\n"); + decompileThread.start(); + } else if (target == saveButton) { + if (frame == null) + frame = new Frame(); //XXX + FileDialog fd = new FileDialog(frame, + "Save decompiled code", + FileDialog.SAVE); + fd.setFile(lastClassName.substring + (lastClassName.lastIndexOf('.')+1).concat(".java")); + fd.show(); + String fileName = fd.getFile(); + if (fileName == null) + return; + try { + File f = new File(new File(fd.getDirectory()), fileName); + FileWriter out = new FileWriter(f); + out.write(sourcecodeArea.getText()); + out.close(); + } catch (IOException ex) { + errorArea.setText(""); + GlobalOptions.err.println("Couldn't write to file " + + fileName + ": "); + ex.printStackTrace(GlobalOptions.err); + } catch (SecurityException ex) { + errorArea.setText(""); + GlobalOptions.err.println("Couldn't write to file " + + fileName + ": "); + ex.printStackTrace(GlobalOptions.err); + } + } + } + + public class AreaWriter extends Writer { + boolean initialized = false; + private TextArea area; + + public AreaWriter(TextArea a) { + area = a; + } + + public void write(char[] b, int off, int len) throws IOException { + if (!initialized) { + area.setText(""); + initialized = true; + } +///#ifdef AWT10 +/// area.appendText(new String(b, off, len)); +///#else + area.append(new String(b, off, len)); +///#endif + } + + public void flush() { + } + + public void close() { + } + } + + public void run() { + GlobalOptions.verboseLevel = verboseCheck.getState() ? 1 : 0; + if (prettyCheck.getState()) + Options.options |= Options.OPTION_PRETTY; + else + Options.options &= ~Options.OPTION_PRETTY; + errorArea.setText(""); +///#ifdef AWT10 +/// saveButton.disable(); +///#else + saveButton.setEnabled(false); +///#endif + + lastClassName = classField.getText(); + ClassInfo.setClassPath(classpathField.getText()); + ImportHandler imports = new ImportHandler(); + try { + ClassInfo clazz; + try { + clazz = ClassInfo.forName(lastClassName); + } catch (IllegalArgumentException ex) { + sourcecodeArea.setText + ("`"+lastClassName+"' is not a class name\n" + +"You have to give a full qualified classname " + +"with '.' as package delimiter \n" + +"and without .class ending"); + return; + } + + TabbedPrintWriter writer = + new TabbedPrintWriter(new AreaWriter(sourcecodeArea), imports); + ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports); + clazzAna.dumpJavaFile(writer); + +///#ifdef AWT10 +/// saveButton.enable(); +///#else + saveButton.setEnabled(true); +///#endif + } catch (Throwable t) { + sourcecodeArea.setText("Didn't succeed.\n" + +"Check the below area for more info."); + t.printStackTrace(); + } finally { + synchronized(this) { + decompileThread = null; +///#ifdef AWT10 +/// startButton.enable(); +///#else + startButton.setEnabled(true); +///#endif + } + } + } + + public static void main(String argv[]) { + Frame frame = new Frame(GlobalOptions.copyright); + Window win = new Window(frame); + + String cp = System.getProperty("java.class.path"); + if (cp != null) + win.setClasspath(cp.replace(File.pathSeparatorChar, + SearchPath.altPathSeparatorChar)); + String cls = win.getClass().getName(); + win.setClass(cls); + +///#ifndef AWT10 + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); +///#endif + frame.pack(); + frame.show(); + } +}