diff --git a/jode/jode/decompiler/Applet.java b/jode/jode/decompiler/Applet.java
index 545d6ae..c758b3d 100644
--- a/jode/jode/decompiler/Applet.java
+++ b/jode/jode/decompiler/Applet.java
@@ -32,10 +32,9 @@ public class Applet extends java.applet.Applet {
public void init() {
String cp = getParameter("classpath");
if (cp != null)
- jodeWin.setClasspath(cp);
+ 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 f83c730..ce89427 100644
--- a/jode/jode/decompiler/ClassAnalyzer.java.in
+++ b/jode/jode/decompiler/ClassAnalyzer.java.in
@@ -30,8 +30,7 @@ import jode.expr.Expression;
import jode.expr.ThisOperator;
import jode.flow.TransformConstructors;
import jode.flow.StructuredBlock;
-import jode.flow.FlowBlock;
-import jode.flow.VariableSet;
+import jode.util.SimpleSet;
import java.lang.reflect.Modifier;
import java.util.NoSuchElementException;
@@ -43,14 +42,26 @@ import @COLLECTIONS@.Collection;
import @COLLECTIONS@.Set;
public class ClassAnalyzer
- implements Analyzer, Scope, Declarable, ClassDeclarer
+ implements Scope, Declarable, ClassDeclarer
{
ImportHandler imports;
ClassInfo clazz;
ClassDeclarer parent;
+ ProgressListener progressListener;
+
+ /**
+ * The complexity for initi#alizing a class.
+ */
+ private static double INITIALIZE_COMPLEXITY = 0.03;
+ /**
+ * The minimal visible complexity.
+ */
+ private static double STEP_COMPLEXITY = 0.03;
+ double methodComplexity = 0.0;
+ double innerComplexity = 0.0;
String name;
- FlowBlock[] blockInitializers;
+ StructuredBlock[] blockInitializers;
FieldAnalyzer[] fields;
MethodAnalyzer[] methods;
ClassAnalyzer[] inners;
@@ -169,8 +180,8 @@ public class ClassAnalyzer
public void addBlockInitializer(int index,
StructuredBlock initializer) {
if (blockInitializers[index] == null)
- blockInitializers[index] = new FlowBlock(null, 0);
- blockInitializers[index].appendBlock(initializer, 0);
+ blockInitializers[index] = initializer;
+ blockInitializers[index].appendBlock(initializer);
}
public void initialize() {
@@ -206,7 +217,7 @@ public class ClassAnalyzer
fields = new FieldAnalyzer[finfos.length];
methods = new MethodAnalyzer[minfos.length];
- blockInitializers = new FlowBlock[finfos.length+1];
+ blockInitializers = new StructuredBlock[finfos.length+1];
for (int j=0; j < finfos.length; j++)
fields[j] = new FieldAnalyzer(this, finfos[j], imports);
@@ -221,6 +232,7 @@ public class ClassAnalyzer
else
constrVector.addElement(methods[j]);
}
+ methodComplexity += methods[j].getComplexity();
}
constructors = new MethodAnalyzer[constrVector.size()];
@@ -229,12 +241,25 @@ public class ClassAnalyzer
// initialize the inner classes.
for (int j=0; j < inners.length; j++) {
inners[j].initialize();
+ innerComplexity += inners[j].getComplexity();
}
}
- public void analyze() {
+ /**
+ * Gets the complexity of this class. Must be called after it has
+ * been initialized. This is used for a nice progress bar.
+ */
+ public double getComplexity() {
+ return (methodComplexity + innerComplexity);
+ }
+
+ public void analyze(ProgressListener pl, double done, double scale) {
if (GlobalOptions.verboseLevel > 0)
- GlobalOptions.err.println("Class " + clazz.getName());
+ GlobalOptions.err.println("Class " + name);
+ double subScale = scale / methodComplexity;
+ if (pl != null)
+ pl.updateProgress(done, name);
+
imports.useClass(clazz);
if (clazz.getSuperclass() != null)
imports.useClass(clazz.getSuperclass());
@@ -254,12 +279,37 @@ public class ClassAnalyzer
constrAna = null;
if (constructors.length > 0) {
for (int j=0; j< constructors.length; j++)
- constructors[j].analyze();
+ {
+ if (pl != null) {
+ double constrCompl = constructors[j].getComplexity()
+ * subScale;
+ if (constrCompl > STEP_COMPLEXITY)
+ constructors[j].analyze(pl, done, constrCompl);
+ else {
+ pl.updateProgress(done, name);
+ constructors[j].analyze(null, 0.0, 0.0);
+ }
+ done += constrCompl;
+ } else
+ constructors[j].analyze(null, 0.0, 0.0);
+ }
constrAna = new TransformConstructors(this, false, constructors);
constrAna.removeSynthInitializers();
}
- if (staticConstructor != null)
- staticConstructor.analyze();
+ if (staticConstructor != null) {
+ if (pl != null) {
+ double constrCompl
+ = staticConstructor.getComplexity() * subScale;
+ if (constrCompl > STEP_COMPLEXITY)
+ staticConstructor.analyze(pl, done, constrCompl);
+ else {
+ pl.updateProgress(done, name);
+ staticConstructor.analyze(null, 0.0, 0.0);
+ }
+ done += constrCompl;
+ } else
+ staticConstructor.analyze(null, 0.0, 0.0);
+ }
// If output should be immediate, we delay analyzation to output.
// Note that this may break anonymous classes, but the user
@@ -274,11 +324,24 @@ public class ClassAnalyzer
// Now analyze remaining methods.
for (int j=0; j < methods.length; j++) {
if (!methods[j].isConstructor())
- methods[j].analyze();
+ if (pl != null) {
+ double methodCompl = methods[j].getComplexity()
+ * subScale;
+ if (methodCompl > STEP_COMPLEXITY)
+ methods[j].analyze(pl, done, methodCompl);
+ else {
+ pl.updateProgress(done, methods[j].getName());
+ methods[j].analyze(null, 0.0, 0.0);
+ }
+ done += methodCompl;
+ } else
+ methods[j].analyze(null, 0.0, 0.0);
}
}
- public void analyzeInnerClasses() {
+ public void analyzeInnerClasses(ProgressListener pl,
+ double done, double scale) {
+ double subScale = scale / innerComplexity;
// If output should be immediate, we delay analyzation to output.
// Note that this may break anonymous classes, but the user
// has been warned.
@@ -287,8 +350,23 @@ public class ClassAnalyzer
// Now analyze the inner classes.
for (int j=0; j < inners.length; j++) {
- inners[j].analyze();
- inners[j].analyzeInnerClasses();
+ if (pl != null) {
+ double innerCompl = inners[j].getComplexity() * subScale;
+ if (innerCompl > STEP_COMPLEXITY) {
+ double innerscale = subScale * inners[j].methodComplexity;
+ inners[j].analyze(pl, done, innerscale);
+ inners[j].analyzeInnerClasses(null, done + innerscale,
+ innerCompl - innerscale);
+ } else {
+ pl.updateProgress(done, inners[j].name);
+ inners[j].analyze(null, 0.0, 0.0);
+ inners[j].analyzeInnerClasses(null, 0.0, 0.0);
+ }
+ done += innerCompl;
+ } else {
+ inners[j].analyze(null, 0.0, 0.0);
+ inners[j].analyzeInnerClasses(null, 0.0, 0.0);
+ }
}
// Now analyze the method scoped classes.
@@ -325,14 +403,23 @@ public class ClassAnalyzer
dumpSource(writer);
}
- public void dumpBlock(TabbedPrintWriter writer) throws IOException
+ public void dumpBlock(TabbedPrintWriter writer)
+ throws IOException
{
+ dumpBlock(writer, null, 0.0, 0.0);
+ }
+
+ public void dumpBlock(TabbedPrintWriter writer,
+ ProgressListener pl, double done, double scale)
+ throws IOException
+ {
+ double subScale = scale / getComplexity();
writer.pushScope(this);
boolean needFieldNewLine = false;
boolean needNewLine = false;
Set declared = null;
if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
- declared = new VariableSet();
+ declared = new SimpleSet();
for (int i=0; i< fields.length; i++) {
if (blockInitializers[i] != null) {
if (needNewLine)
@@ -372,19 +459,29 @@ public class ClassAnalyzer
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
// We now do the analyzation we skipped before.
- inners[i].analyze();
- inners[i].analyzeInnerClasses();
+ inners[i].analyze(null, 0.0, 0.0);
+ inners[i].analyzeInnerClasses(null, 0.0, 0.0);
inners[i].makeDeclaration(declared);
}
- inners[i].dumpSource(writer);
+ if (pl != null) {
+ double innerCompl = inners[i].getComplexity() * subScale;
+ if (innerCompl > STEP_COMPLEXITY)
+ inners[i].dumpSource(writer, pl, done, innerCompl);
+ else {
+ pl.updateProgress(done, name);
+ inners[i].dumpSource(writer);
+ }
+ done += innerCompl;
+ } else
+ inners[i].dumpSource(writer);
needNewLine = true;
}
for (int i=0; i< methods.length; i++) {
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
// We now do the analyzation we skipped before.
if (!methods[i].isConstructor())
- methods[i].analyze();
+ methods[i].analyze(null, 0.0, 0.0);
methods[i].analyzeInnerClasses();
methods[i].makeDeclaration(declared);
}
@@ -393,13 +490,28 @@ public class ClassAnalyzer
continue;
if (needNewLine)
writer.println("");
- methods[i].dumpSource(writer);
+
+ if (pl != null) {
+ double methodCompl = methods[i].getComplexity() * subScale;
+ pl.updateProgress(done, methods[i].getName());
+ methods[i].dumpSource(writer);
+ done += methodCompl;
+ } else
+ methods[i].dumpSource(writer);
needNewLine = true;
}
writer.popScope();
}
- public void dumpSource(TabbedPrintWriter writer) throws IOException
+ public void dumpSource(TabbedPrintWriter writer)
+ throws IOException
+ {
+ dumpSource(writer, null, 0.0, 0.0);
+ }
+
+ public void dumpSource(TabbedPrintWriter writer,
+ ProgressListener pl, double done, double scale)
+ throws IOException
{
if (fields == null) {
/* This means that the class could not be loaded.
@@ -449,7 +561,7 @@ public class ClassAnalyzer
writer.openBrace();
writer.tab();
- dumpBlock(writer);
+ dumpBlock(writer, pl, done, scale);
writer.untab();
if (parent instanceof MethodAnalyzer) {
/* This is a method scope class */
@@ -459,16 +571,27 @@ public class ClassAnalyzer
clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS);
}
- public void dumpJavaFile(TabbedPrintWriter writer) throws IOException {
+ public void dumpJavaFile(TabbedPrintWriter writer)
+ throws IOException {
+ dumpJavaFile(writer, null);
+ }
+
+ public void dumpJavaFile(TabbedPrintWriter writer, ProgressListener pl)
+ throws IOException {
imports.init(clazz.getName());
LocalInfo.init();
initialize();
- analyze();
- analyzeInnerClasses();
- makeDeclaration(new VariableSet());
-
+ double done = 0.05;
+ double scale = (0.75) * methodComplexity
+ / (methodComplexity + innerComplexity);
+ analyze(pl, INITIALIZE_COMPLEXITY, scale);
+ done += scale;
+ analyzeInnerClasses(pl, done, 0.8 - done);
+ makeDeclaration(new SimpleSet());
imports.dumpHeader(writer);
- dumpSource(writer);
+ dumpSource(writer, pl, 0.8, 0.2);
+ if (pl != null)
+ pl.updateProgress(1.0, name);
}
public boolean isScopeOf(Object obj, int scopeType) {
diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java
new file mode 100644
index 0000000..937cc33
--- /dev/null
+++ b/jode/jode/decompiler/Decompiler.java
@@ -0,0 +1,193 @@
+/* Decompiler Copyright (C) 2000 Jochen Hoenicke.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+package jode.decompiler;
+import jode.GlobalOptions;
+import jode.bytecode.SearchPath;
+import jode.bytecode.ClassInfo;
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.io.BufferedWriter;
+
+/**
+ * This is the interface that other java classes may use to decompile
+ * classes. Feel free to use it in your own GNU GPL'ed project.
+ * Please tell me about your project.
+ *
+ * Note that the GNU GPL doesn't allow you to use this interface in
+ * commercial programs.
+ *
+ * @author Jochen Hoenicke
+ * @version 1.0
+ */
+public class Decompiler {
+ private SearchPath searchPath = null;
+ private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
+ private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;
+
+ /**
+ * We need a different pathSeparatorChar, since ':' (used for most
+ * UNIX System) is used a protocol separator in URLs.
+ *
+ * We currently allow both pathSeparatorChar and
+ * altPathSeparatorChar and decide if it is a protocol separator
+ * by context.
+ */
+ public static final char altPathSeparatorChar
+ = SearchPath.altPathSeparatorChar;
+
+ /**
+ * Create a new decompiler.
+ */
+ public Decompiler() {
+ }
+
+ /**
+ * Sets the class path. Should be called once before decompile is
+ * called, otherwise the system class path is used.
+ * @param classpath A comma separated classpath.
+ * @exception NullPointerException if classpath is null.
+ * @see #setClassPath(String[])
+ */
+ public void setClassPath(String classpath) {
+ searchPath = new SearchPath(classpath);
+ }
+
+ /**
+ * Set the class path. Should be called once before decompile is
+ * called, otherwise the system class path is used.
+ * @param classpath a non empty array of jar files and directories;
+ * URLs are allowed, too.
+ * @exception NullPointerException if classpath is null.
+ * @exception IndexOutOfBoundsException if classpath array is empty.
+ * @see #setClassPath(String)
+ */
+ public void setClassPath(String[] classpath) {
+ StringBuffer sb = new StringBuffer(classpath[0]);
+ for (int i = 1; i < classpath.length; i++)
+ sb.append(altPathSeparatorChar).append(classpath[i]);
+ searchPath = new SearchPath(sb.toString());
+ }
+
+ private static final String[] optionStrings = {
+ "lvt", "inner", "anonymous", "push", "pretty", "decrypt",
+ "onetime", "immediate", "verify", "contrafo"
+ };
+
+ /**
+ * Set an option.
+ * @param option the option (pretty, style, decrypt, verify, etc.)
+ * @param value ("1"/"0" for on/off, "sun"/"gnu" for style)
+ * @exception IllegalArgumentException if option or value is invalid.
+ */
+ public void setOption(String option, String value) {
+ if (option.equals("style")) {
+ if (value.equals("gnu"))
+ Options.outputStyle = Options.GNU_STYLE;
+ else if (value.equals("sun"))
+ Options.outputStyle = Options.SUN_STYLE;
+ else
+ throw new IllegalArgumentException("Invalid style "+value);
+ return;
+ }
+ if (option.equals("import")) {
+ int comma = value.indexOf(',');
+ int packLimit = Integer.parseInt(value.substring(0, comma));
+ if (packLimit == 0)
+ packLimit = Integer.MAX_VALUE;
+ int clazzLimit = Integer.parseInt(value.substring(comma+1));
+ if (clazzLimit == 0)
+ clazzLimit = Integer.MAX_VALUE;
+ if (clazzLimit < 0 || packLimit < 0)
+ throw new IllegalArgumentException
+ ("Option import doesn't allow negative parameters");
+ importPackageLimit = packLimit;
+ importClassLimit = clazzLimit;
+ return;
+ }
+ if (option.equals("verbose")) {
+ GlobalOptions.verboseLevel = Integer.parseInt(value);
+ return;
+ }
+ for (int i=0; i < optionStrings.length; i++) {
+ if (option.equals(optionStrings[i])) {
+ if (value.equals("0")
+ || value.equals("off")
+ || value.equals("no"))
+ Options.options &= ~(1 << i);
+ else if (value.equals("1")
+ || value.equals("on")
+ || value.equals("yes"))
+ Options.options &= ~(1 << i);
+ else
+ throw new IllegalArgumentException("Illegal value for "+
+ option);
+ return;
+ }
+ }
+ throw new IllegalArgumentException("Illegal option: "+option);
+ }
+
+
+ /**
+ * Set the stream where copyright and warnings/errors are printed
+ * to.
+ * @param errorStream the error stream. Note that this is a
+ * PrintWriter, not a PrintStream (which are deprecated since 1.1).
+ */
+ public void setErr(PrintWriter errorStream) {
+ GlobalOptions.err = errorStream;
+ }
+
+ /**
+ * Decompile a class.
+ * @param className full-qualified classname, dot separated, e.g.
+ * "java.lang.Object"
+ * @param writer The stream where the decompiled code should be
+ * written. Hint: Use a BufferedWriter for good performance.
+ * @param progress A progress listener (see below). Null if you
+ * don't need information about progress.
+ * @exception IllegalArgumentException if className isn't correct.
+ * @exception jode.jvm.VerifyException The code
+ * isn't valid or a dependent class, needed for type
+ * guessing, couldn't be found.
+ * @exception IOException if writer throws an exception.
+ * @exception RuntimeException If jode has a bug ;-)
+ */
+ public void decompile(String className, Writer writer,
+ ProgressListener progress)
+ throws java.io.IOException {
+ if (searchPath == null) {
+ String classPath = System.getProperty("java.class.path")
+ .replace(File.pathSeparatorChar, altPathSeparatorChar);
+ searchPath = new SearchPath(classPath);
+ }
+
+ ClassInfo.setClassPath(searchPath);
+ ClassInfo clazz = ClassInfo.forName(className);
+ ImportHandler imports = new ImportHandler(importPackageLimit,
+ importClassLimit);
+ TabbedPrintWriter tabbedWriter =
+ new TabbedPrintWriter(writer, imports, false);
+ ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
+ clazzAna.dumpJavaFile(tabbedWriter, progress);
+ writer.flush();
+ }
+}
diff --git a/jode/jode/decompiler/Makefile.am b/jode/jode/decompiler/Makefile.am
index be2ae92..a67e4ce 100644
--- a/jode/jode/decompiler/Makefile.am
+++ b/jode/jode/decompiler/Makefile.am
@@ -8,7 +8,7 @@ JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
-BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB)
+FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \
Analyzer.java \
@@ -17,6 +17,7 @@ MY_JAVA_FILES = \
ClassDeclarer.java \
DeadCodeAnalysis.java \
Declarable.java \
+ Decompiler.java \
FieldAnalyzer.java \
ImportHandler.java \
LocalInfo.java \
@@ -29,6 +30,7 @@ MY_JAVA_FILES = \
Options.java \
OuterValueListener.java \
OuterValues.java \
+ ProgressListener.java \
Scope.java \
TabbedPrintWriter.java \
Window.java
@@ -39,7 +41,7 @@ EXTRA_DIST = $(MY_JAVA_FILES)
@QUOTE@-include Makefile.dep
%.class: %.java
- $(JAVAC) -classpath `$(SUBSTCP) $(BUILD_CLASSPATH):$(CLASSLIB)` -d $(top_builddir) $<
+ $(JAVAC) -classpath $(FULL_CLASSPATH) -d $(top_builddir) $<
Makefile.dep: $(MY_JAVA_FILES:.java=.class)
$(JAVADEP) $^
diff --git a/jode/jode/decompiler/MethodAnalyzer.java.in b/jode/jode/decompiler/MethodAnalyzer.java.in
index b13a8ca..0092891 100644
--- a/jode/jode/decompiler/MethodAnalyzer.java.in
+++ b/jode/jode/decompiler/MethodAnalyzer.java.in
@@ -76,6 +76,10 @@ import @COLLECTIONS@.Set;
* it will do special transformations like field initialization.
*/
public class MethodAnalyzer implements Scope, ClassDeclarer {
+ /**
+ * The minimal visible complexity.
+ */
+ private static double STEP_COMPLEXITY = 0.01;
/**
* The import handler where we should register our types.
*/
@@ -199,24 +203,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (minfo.getBytecode() != null) {
code = minfo.getBytecode();
-
- if ((Options.options & Options.OPTION_VERIFY) != 0) {
- CodeVerifier verifier
- = new CodeVerifier(getClazz(), minfo, code);
- try {
- verifier.verify();
- } catch (VerifyException ex) {
- ex.printStackTrace(GlobalOptions.err);
- throw new jode.AssertError("Verification error");
- }
- }
-
- if ((Options.options & Options.OPTION_LVT) != 0) {
- LocalVariableInfo[] localvars = code.getLocalVariableTable();
- if (localvars != null)
- lvt = new LocalVariableTable(code.getMaxLocals(),
- localvars);
- }
}
String[] excattr = minfo.getExceptions();
if (excattr == null) {
@@ -440,14 +426,32 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
return li;
}
+ /**
+ * Gets the complexity of this class. Must be called after it has
+ * been initialized. This is used for a nice progress bar.
+ */
+ public double getComplexity() {
+ if (code == null)
+ return 0.0;
+ else
+ return code.getInstructions().size();
+ }
+
/**
* Analyzes the code of this method. This creates the
* flow blocks (including methodHeader) and analyzes them.
*/
- private void analyzeCode()
+ private void analyzeCode(ProgressListener pl, double done, double scale)
{
+ int instrsPerStep = Integer.MAX_VALUE;
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": ");
+
+ if (pl != null) {
+ instrsPerStep = (int) ((code.getInstructions().size()
+ * STEP_COMPLEXITY) / (scale * 0.9));
+ }
+
/* The adjacent analyzation relies on this */
DeadCodeAnalysis.removeDeadCode(code);
Handler[] handlers = code.getExceptionHandlers();
@@ -487,6 +491,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* second has no predecessors.
*/
int mark = 1000;
+ int count = 0;
FlowBlock lastBlock = null;
boolean lastSequential = false;
for (Iterator i = code.getInstructions().iterator();
@@ -500,6 +505,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
GlobalOptions.err.print('.');
mark += 1000;
}
+ if (++count >= instrsPerStep) {
+ done += count * scale / code.getInstructions().size();
+ pl.updateProgress(done, methodName);
+ count = 0;
+ }
+
if (lastSequential && instr.getTmpInfo() == null
/* Only merge with previous block, if this is sequential,
@@ -562,15 +573,41 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("");
+ if (pl != null) {
+ done += 0.1 * scale;
+ pl.updateProgress(done, methodName);
+ }
}
/**
* This is the first pass of the analyzation. It will analyze the
* code of this method, but not the method scoped classes.
*/
- public void analyze()
+ public void analyze(ProgressListener pl, double done, double scale)
throws ClassFormatError
{
+ if (pl != null)
+ pl.updateProgress(done, methodName);
+ if (code != null) {
+ if ((Options.options & Options.OPTION_VERIFY) != 0) {
+ CodeVerifier verifier
+ = new CodeVerifier(getClazz(), minfo, code);
+ try {
+ verifier.verify();
+ } catch (VerifyException ex) {
+ ex.printStackTrace(GlobalOptions.err);
+ throw new jode.AssertError("Verification error");
+ }
+ }
+
+ if ((Options.options & Options.OPTION_LVT) != 0) {
+ LocalVariableInfo[] localvars = code.getLocalVariableTable();
+ if (localvars != null)
+ lvt = new LocalVariableTable(code.getMaxLocals(),
+ localvars);
+ }
+ }
+
Type[] paramTypes = getType().getParameterTypes();
int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
param = new LocalInfo[paramCount];
@@ -598,14 +635,14 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
imports.useType(methodType.getReturnType());
if (code != null)
- analyzeCode();
+ analyzeCode(pl, done, scale);
}
/**
* This is the second pass of the analyzation. It will analyze
* the method scoped classes.
*/
- public void analyzeInnerClasses()
+ public void analyzeInnerClasses()
throws ClassFormatError
{
int serialnr = 0;
@@ -984,8 +1021,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
outerValueArray);
addClassAnalyzer(anonAnalyzer);
anonAnalyzer.initialize();
- anonAnalyzer.analyze();
- anonAnalyzer.analyzeInnerClasses();
+ anonAnalyzer.analyze(null, 0.0, 0.0);
+ anonAnalyzer.analyzeInnerClasses(null, 0.0, 0.0);
} else {
/*
* Get the previously created outerValues and
diff --git a/jode/jode/decompiler/Options.java b/jode/jode/decompiler/Options.java
index e7f0661..a083181 100644
--- a/jode/jode/decompiler/Options.java
+++ b/jode/jode/decompiler/Options.java
@@ -39,7 +39,7 @@ public class Options {
public static final int OPTION_CONTRAFO = 0x0200;
public static int options =
- OPTION_LVT | OPTION_INNER | OPTION_ANON |
+ OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY |
OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO;
public static int outputStyle = SUN_STYLE;
diff --git a/jode/jode/decompiler/ProgressListener.java b/jode/jode/decompiler/ProgressListener.java
new file mode 100644
index 0000000..c1f08f1
--- /dev/null
+++ b/jode/jode/decompiler/ProgressListener.java
@@ -0,0 +1,44 @@
+/* ProgressListener Copyright (C) 2000 Jochen Hoenicke.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+package jode.decompiler;
+import jode.GlobalOptions;
+import jode.bytecode.SearchPath;
+import jode.bytecode.ClassInfo;
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.io.BufferedWriter;
+
+/**
+ * This interface is used by jode to tell about its progress. You
+ * supply an instance of this interface to the
+ * {@link Decompiler.decompile} method.
+ *
+ * @author Jochen Hoenicke
+ * @version 1.0 */
+public interface ProgressListener {
+ /**
+ * Gets called when jode makes some progress.
+ * @param progress A number between 0.0 and 1.0
+ * @param detail
+ * The name of the currently decompiled method or class.
+ */
+ public void updateProgress(double progress, String detail);
+}
diff --git a/jode/jode/decompiler/Window.java b/jode/jode/decompiler/Window.java
index 6002253..3478633 100644
--- a/jode/jode/decompiler/Window.java
+++ b/jode/jode/decompiler/Window.java
@@ -24,8 +24,6 @@ import java.awt.*;
import java.awt.event.*;
///#endif
import java.io.*;
-import jode.bytecode.ClassInfo;
-import jode.bytecode.SearchPath;
import jode.GlobalOptions;
public class Window
@@ -42,6 +40,8 @@ public class Window
String lastClassName;
Frame frame;
+ PrintWriter errStream;
+ Decompiler decompiler = new Decompiler();
Thread decompileThread;
public Window(Container window) {
@@ -147,10 +147,11 @@ public class Window
startButton.addActionListener(this);
saveButton.addActionListener(this);
///#endif
- GlobalOptions.err = new PrintWriter(new AreaWriter(errorArea));
+ errStream = new PrintWriter(new AreaWriter(errorArea));
+ decompiler.setErr(errStream);
}
- public void setClasspath(String cp) {
+ public void setClassPath(String cp) {
classpathField.setText(cp);
}
public void setClass(String cls) {
@@ -192,14 +193,14 @@ public class Window
out.close();
} catch (IOException ex) {
errorArea.setText("");
- GlobalOptions.err.println("Couldn't write to file "
+ errStream.println("Couldn't write to file "
+ fileName + ": ");
- ex.printStackTrace(GlobalOptions.err);
+ ex.printStackTrace(errStream);
} catch (SecurityException ex) {
errorArea.setText("");
- GlobalOptions.err.println("Couldn't write to file "
+ errStream.println("Couldn't write to file "
+ fileName + ": ");
- ex.printStackTrace(GlobalOptions.err);
+ ex.printStackTrace(errStream);
}
}
}
@@ -232,11 +233,11 @@ public class Window
}
public void run() {
- GlobalOptions.verboseLevel = verboseCheck.getState() ? 1 : 0;
+ decompiler.setOption("verbose", verboseCheck.getState() ? "1" : "0");
if (prettyCheck.getState())
- Options.options |= Options.OPTION_PRETTY;
+ decompiler.setOption("pretty", "0");
else
- Options.options &= ~Options.OPTION_PRETTY;
+ decompiler.setOption("pretty", "1");
errorArea.setText("");
///#ifdef AWT10
/// saveButton.disable();
@@ -245,12 +246,11 @@ public class Window
///#endif
lastClassName = classField.getText();
- ClassInfo.setClassPath(classpathField.getText());
- ImportHandler imports = new ImportHandler();
+ decompiler.setClassPath(classpathField.getText());
try {
- ClassInfo clazz;
+ Writer writer = new AreaWriter(sourcecodeArea);
try {
- clazz = ClassInfo.forName(lastClassName);
+ decompiler.decompile(lastClassName, writer, null);
} catch (IllegalArgumentException ex) {
sourcecodeArea.setText
("`"+lastClassName+"' is not a class name\n"
@@ -259,12 +259,6 @@ public class Window
+"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
@@ -292,8 +286,8 @@ public class Window
String cp = System.getProperty("java.class.path");
if (cp != null)
- win.setClasspath(cp.replace(File.pathSeparatorChar,
- SearchPath.altPathSeparatorChar));
+ win.setClassPath(cp.replace(File.pathSeparatorChar,
+ Decompiler.altPathSeparatorChar));
String cls = win.getClass().getName();
win.setClass(cls);