New Decompiler/ProgressListener interface

bug fix in blockInitializers (now uses StructuredBlocks)
Makefile optimized


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1217 379699f6-c40d-0410-875b-85095c16579e
branch_1_1
jochen 25 years ago
parent c24a2b7133
commit 9ce95e120a
  1. 3
      jode/jode/decompiler/Applet.java
  2. 185
      jode/jode/decompiler/ClassAnalyzer.java.in
  3. 193
      jode/jode/decompiler/Decompiler.java
  4. 6
      jode/jode/decompiler/Makefile.am
  5. 85
      jode/jode/decompiler/MethodAnalyzer.java.in
  6. 2
      jode/jode/decompiler/Options.java
  7. 44
      jode/jode/decompiler/ProgressListener.java
  8. 40
      jode/jode/decompiler/Window.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);
}
}

@ -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) {

@ -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.<br>
*
* Note that the GNU GPL doesn't allow you to use this interface in
* commercial programs.
*
* @author <a href="mailto:jochen@gnu.org">Jochen Hoenicke</a>
* @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();
}
}

@ -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) $^

@ -76,6 +76,10 @@ import @COLLECTIONS@.Set;
* it will do special transformations like field initialization.</dd>
*/
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

@ -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;

@ -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.<br>
*
* @author <a href="mailto:jochen@gnu.org">Jochen Hoenicke</a>
* @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);
}

@ -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);

Loading…
Cancel
Save