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. 181
      jode/jode/decompiler/ClassAnalyzer.java.in
  3. 193
      jode/jode/decompiler/Decompiler.java
  4. 6
      jode/jode/decompiler/Makefile.am
  5. 83
      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() { public void init() {
String cp = getParameter("classpath"); String cp = getParameter("classpath");
if (cp != null) if (cp != null)
jodeWin.setClasspath(cp); jodeWin.setClassPath(cp);
String cls = getParameter("class"); String cls = getParameter("class");
if (cls != null) if (cls != null)
jodeWin.setClass(cls); jodeWin.setClass(cls);
} }
} }

@ -30,8 +30,7 @@ import jode.expr.Expression;
import jode.expr.ThisOperator; import jode.expr.ThisOperator;
import jode.flow.TransformConstructors; import jode.flow.TransformConstructors;
import jode.flow.StructuredBlock; import jode.flow.StructuredBlock;
import jode.flow.FlowBlock; import jode.util.SimpleSet;
import jode.flow.VariableSet;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -43,14 +42,26 @@ import @COLLECTIONS@.Collection;
import @COLLECTIONS@.Set; import @COLLECTIONS@.Set;
public class ClassAnalyzer public class ClassAnalyzer
implements Analyzer, Scope, Declarable, ClassDeclarer implements Scope, Declarable, ClassDeclarer
{ {
ImportHandler imports; ImportHandler imports;
ClassInfo clazz; ClassInfo clazz;
ClassDeclarer parent; 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; String name;
FlowBlock[] blockInitializers; StructuredBlock[] blockInitializers;
FieldAnalyzer[] fields; FieldAnalyzer[] fields;
MethodAnalyzer[] methods; MethodAnalyzer[] methods;
ClassAnalyzer[] inners; ClassAnalyzer[] inners;
@ -169,8 +180,8 @@ public class ClassAnalyzer
public void addBlockInitializer(int index, public void addBlockInitializer(int index,
StructuredBlock initializer) { StructuredBlock initializer) {
if (blockInitializers[index] == null) if (blockInitializers[index] == null)
blockInitializers[index] = new FlowBlock(null, 0); blockInitializers[index] = initializer;
blockInitializers[index].appendBlock(initializer, 0); blockInitializers[index].appendBlock(initializer);
} }
public void initialize() { public void initialize() {
@ -206,7 +217,7 @@ public class ClassAnalyzer
fields = new FieldAnalyzer[finfos.length]; fields = new FieldAnalyzer[finfos.length];
methods = new MethodAnalyzer[minfos.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++) for (int j=0; j < finfos.length; j++)
fields[j] = new FieldAnalyzer(this, finfos[j], imports); fields[j] = new FieldAnalyzer(this, finfos[j], imports);
@ -221,6 +232,7 @@ public class ClassAnalyzer
else else
constrVector.addElement(methods[j]); constrVector.addElement(methods[j]);
} }
methodComplexity += methods[j].getComplexity();
} }
constructors = new MethodAnalyzer[constrVector.size()]; constructors = new MethodAnalyzer[constrVector.size()];
@ -229,12 +241,25 @@ public class ClassAnalyzer
// initialize the inner classes. // initialize the inner classes.
for (int j=0; j < inners.length; j++) { for (int j=0; j < inners.length; j++) {
inners[j].initialize(); 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) 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); imports.useClass(clazz);
if (clazz.getSuperclass() != null) if (clazz.getSuperclass() != null)
imports.useClass(clazz.getSuperclass()); imports.useClass(clazz.getSuperclass());
@ -254,12 +279,37 @@ public class ClassAnalyzer
constrAna = null; constrAna = null;
if (constructors.length > 0) { if (constructors.length > 0) {
for (int j=0; j< constructors.length; j++) 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 = new TransformConstructors(this, false, constructors);
constrAna.removeSynthInitializers(); constrAna.removeSynthInitializers();
} }
if (staticConstructor != null) if (staticConstructor != null) {
staticConstructor.analyze(); 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. // If output should be immediate, we delay analyzation to output.
// Note that this may break anonymous classes, but the user // Note that this may break anonymous classes, but the user
@ -274,11 +324,24 @@ public class ClassAnalyzer
// Now analyze remaining methods. // Now analyze remaining methods.
for (int j=0; j < methods.length; j++) { for (int j=0; j < methods.length; j++) {
if (!methods[j].isConstructor()) 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. // If output should be immediate, we delay analyzation to output.
// Note that this may break anonymous classes, but the user // Note that this may break anonymous classes, but the user
// has been warned. // has been warned.
@ -287,8 +350,23 @@ public class ClassAnalyzer
// Now analyze the inner classes. // Now analyze the inner classes.
for (int j=0; j < inners.length; j++) { for (int j=0; j < inners.length; j++) {
inners[j].analyze(); if (pl != null) {
inners[j].analyzeInnerClasses(); 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. // Now analyze the method scoped classes.
@ -325,14 +403,23 @@ public class ClassAnalyzer
dumpSource(writer); 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); writer.pushScope(this);
boolean needFieldNewLine = false; boolean needFieldNewLine = false;
boolean needNewLine = false; boolean needNewLine = false;
Set declared = null; Set declared = null;
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
declared = new VariableSet(); declared = new SimpleSet();
for (int i=0; i< fields.length; i++) { for (int i=0; i< fields.length; i++) {
if (blockInitializers[i] != null) { if (blockInitializers[i] != null) {
if (needNewLine) if (needNewLine)
@ -372,11 +459,21 @@ public class ClassAnalyzer
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
// We now do the analyzation we skipped before. // We now do the analyzation we skipped before.
inners[i].analyze(); inners[i].analyze(null, 0.0, 0.0);
inners[i].analyzeInnerClasses(); inners[i].analyzeInnerClasses(null, 0.0, 0.0);
inners[i].makeDeclaration(declared); inners[i].makeDeclaration(declared);
} }
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); inners[i].dumpSource(writer);
needNewLine = true; needNewLine = true;
} }
@ -384,7 +481,7 @@ public class ClassAnalyzer
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
// We now do the analyzation we skipped before. // We now do the analyzation we skipped before.
if (!methods[i].isConstructor()) if (!methods[i].isConstructor())
methods[i].analyze(); methods[i].analyze(null, 0.0, 0.0);
methods[i].analyzeInnerClasses(); methods[i].analyzeInnerClasses();
methods[i].makeDeclaration(declared); methods[i].makeDeclaration(declared);
} }
@ -393,13 +490,28 @@ public class ClassAnalyzer
continue; continue;
if (needNewLine) if (needNewLine)
writer.println(""); writer.println("");
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); methods[i].dumpSource(writer);
needNewLine = true; needNewLine = true;
} }
writer.popScope(); 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) { if (fields == null) {
/* This means that the class could not be loaded. /* This means that the class could not be loaded.
@ -449,7 +561,7 @@ public class ClassAnalyzer
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();
dumpBlock(writer); dumpBlock(writer, pl, done, scale);
writer.untab(); writer.untab();
if (parent instanceof MethodAnalyzer) { if (parent instanceof MethodAnalyzer) {
/* This is a method scope class */ /* This is a method scope class */
@ -459,16 +571,27 @@ public class ClassAnalyzer
clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS); 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()); imports.init(clazz.getName());
LocalInfo.init(); LocalInfo.init();
initialize(); initialize();
analyze(); double done = 0.05;
analyzeInnerClasses(); double scale = (0.75) * methodComplexity
makeDeclaration(new VariableSet()); / (methodComplexity + innerComplexity);
analyze(pl, INITIALIZE_COMPLEXITY, scale);
done += scale;
analyzeInnerClasses(pl, done, 0.8 - done);
makeDeclaration(new SimpleSet());
imports.dumpHeader(writer); 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) { 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@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@
BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB) FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB))
MY_JAVA_FILES = \ MY_JAVA_FILES = \
Analyzer.java \ Analyzer.java \
@ -17,6 +17,7 @@ MY_JAVA_FILES = \
ClassDeclarer.java \ ClassDeclarer.java \
DeadCodeAnalysis.java \ DeadCodeAnalysis.java \
Declarable.java \ Declarable.java \
Decompiler.java \
FieldAnalyzer.java \ FieldAnalyzer.java \
ImportHandler.java \ ImportHandler.java \
LocalInfo.java \ LocalInfo.java \
@ -29,6 +30,7 @@ MY_JAVA_FILES = \
Options.java \ Options.java \
OuterValueListener.java \ OuterValueListener.java \
OuterValues.java \ OuterValues.java \
ProgressListener.java \
Scope.java \ Scope.java \
TabbedPrintWriter.java \ TabbedPrintWriter.java \
Window.java Window.java
@ -39,7 +41,7 @@ EXTRA_DIST = $(MY_JAVA_FILES)
@QUOTE@-include Makefile.dep @QUOTE@-include Makefile.dep
%.class: %.java %.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) Makefile.dep: $(MY_JAVA_FILES:.java=.class)
$(JAVADEP) $^ $(JAVADEP) $^

@ -76,6 +76,10 @@ import @COLLECTIONS@.Set;
* it will do special transformations like field initialization.</dd> * it will do special transformations like field initialization.</dd>
*/ */
public class MethodAnalyzer implements Scope, ClassDeclarer { 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. * The import handler where we should register our types.
*/ */
@ -199,24 +203,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (minfo.getBytecode() != null) { if (minfo.getBytecode() != null) {
code = minfo.getBytecode(); 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(); String[] excattr = minfo.getExceptions();
if (excattr == null) { if (excattr == null) {
@ -440,14 +426,32 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
return li; 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 * Analyzes the code of this method. This creates the
* flow blocks (including methodHeader) and analyzes them. * 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) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": "); GlobalOptions.err.print(methodName+": ");
if (pl != null) {
instrsPerStep = (int) ((code.getInstructions().size()
* STEP_COMPLEXITY) / (scale * 0.9));
}
/* The adjacent analyzation relies on this */ /* The adjacent analyzation relies on this */
DeadCodeAnalysis.removeDeadCode(code); DeadCodeAnalysis.removeDeadCode(code);
Handler[] handlers = code.getExceptionHandlers(); Handler[] handlers = code.getExceptionHandlers();
@ -487,6 +491,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* second has no predecessors. * second has no predecessors.
*/ */
int mark = 1000; int mark = 1000;
int count = 0;
FlowBlock lastBlock = null; FlowBlock lastBlock = null;
boolean lastSequential = false; boolean lastSequential = false;
for (Iterator i = code.getInstructions().iterator(); for (Iterator i = code.getInstructions().iterator();
@ -500,6 +505,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
GlobalOptions.err.print('.'); GlobalOptions.err.print('.');
mark += 1000; mark += 1000;
} }
if (++count >= instrsPerStep) {
done += count * scale / code.getInstructions().size();
pl.updateProgress(done, methodName);
count = 0;
}
if (lastSequential && instr.getTmpInfo() == null if (lastSequential && instr.getTmpInfo() == null
/* Only merge with previous block, if this is sequential, /* Only merge with previous block, if this is sequential,
@ -562,15 +573,41 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println(""); 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 * This is the first pass of the analyzation. It will analyze the
* code of this method, but not the method scoped classes. * code of this method, but not the method scoped classes.
*/ */
public void analyze() public void analyze(ProgressListener pl, double done, double scale)
throws ClassFormatError 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(); Type[] paramTypes = getType().getParameterTypes();
int paramCount = (isStatic() ? 0 : 1) + paramTypes.length; int paramCount = (isStatic() ? 0 : 1) + paramTypes.length;
param = new LocalInfo[paramCount]; param = new LocalInfo[paramCount];
@ -598,7 +635,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
imports.useType(methodType.getReturnType()); imports.useType(methodType.getReturnType());
if (code != null) if (code != null)
analyzeCode(); analyzeCode(pl, done, scale);
} }
/** /**
@ -984,8 +1021,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
outerValueArray); outerValueArray);
addClassAnalyzer(anonAnalyzer); addClassAnalyzer(anonAnalyzer);
anonAnalyzer.initialize(); anonAnalyzer.initialize();
anonAnalyzer.analyze(); anonAnalyzer.analyze(null, 0.0, 0.0);
anonAnalyzer.analyzeInnerClasses(); anonAnalyzer.analyzeInnerClasses(null, 0.0, 0.0);
} else { } else {
/* /*
* Get the previously created outerValues and * Get the previously created outerValues and

@ -39,7 +39,7 @@ public class Options {
public static final int OPTION_CONTRAFO = 0x0200; public static final int OPTION_CONTRAFO = 0x0200;
public static int options = public static int options =
OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY |
OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO; OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO;
public static int outputStyle = SUN_STYLE; 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.*; import java.awt.event.*;
///#endif ///#endif
import java.io.*; import java.io.*;
import jode.bytecode.ClassInfo;
import jode.bytecode.SearchPath;
import jode.GlobalOptions; import jode.GlobalOptions;
public class Window public class Window
@ -42,6 +40,8 @@ public class Window
String lastClassName; String lastClassName;
Frame frame; Frame frame;
PrintWriter errStream;
Decompiler decompiler = new Decompiler();
Thread decompileThread; Thread decompileThread;
public Window(Container window) { public Window(Container window) {
@ -147,10 +147,11 @@ public class Window
startButton.addActionListener(this); startButton.addActionListener(this);
saveButton.addActionListener(this); saveButton.addActionListener(this);
///#endif ///#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); classpathField.setText(cp);
} }
public void setClass(String cls) { public void setClass(String cls) {
@ -192,14 +193,14 @@ public class Window
out.close(); out.close();
} catch (IOException ex) { } catch (IOException ex) {
errorArea.setText(""); errorArea.setText("");
GlobalOptions.err.println("Couldn't write to file " errStream.println("Couldn't write to file "
+ fileName + ": "); + fileName + ": ");
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(errStream);
} catch (SecurityException ex) { } catch (SecurityException ex) {
errorArea.setText(""); errorArea.setText("");
GlobalOptions.err.println("Couldn't write to file " errStream.println("Couldn't write to file "
+ fileName + ": "); + fileName + ": ");
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(errStream);
} }
} }
} }
@ -232,11 +233,11 @@ public class Window
} }
public void run() { public void run() {
GlobalOptions.verboseLevel = verboseCheck.getState() ? 1 : 0; decompiler.setOption("verbose", verboseCheck.getState() ? "1" : "0");
if (prettyCheck.getState()) if (prettyCheck.getState())
Options.options |= Options.OPTION_PRETTY; decompiler.setOption("pretty", "0");
else else
Options.options &= ~Options.OPTION_PRETTY; decompiler.setOption("pretty", "1");
errorArea.setText(""); errorArea.setText("");
///#ifdef AWT10 ///#ifdef AWT10
/// saveButton.disable(); /// saveButton.disable();
@ -245,12 +246,11 @@ public class Window
///#endif ///#endif
lastClassName = classField.getText(); lastClassName = classField.getText();
ClassInfo.setClassPath(classpathField.getText()); decompiler.setClassPath(classpathField.getText());
ImportHandler imports = new ImportHandler();
try { try {
ClassInfo clazz; Writer writer = new AreaWriter(sourcecodeArea);
try { try {
clazz = ClassInfo.forName(lastClassName); decompiler.decompile(lastClassName, writer, null);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
sourcecodeArea.setText sourcecodeArea.setText
("`"+lastClassName+"' is not a class name\n" ("`"+lastClassName+"' is not a class name\n"
@ -259,12 +259,6 @@ public class Window
+"and without .class ending"); +"and without .class ending");
return; return;
} }
TabbedPrintWriter writer =
new TabbedPrintWriter(new AreaWriter(sourcecodeArea), imports);
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
clazzAna.dumpJavaFile(writer);
///#ifdef AWT10 ///#ifdef AWT10
/// saveButton.enable(); /// saveButton.enable();
///#else ///#else
@ -292,8 +286,8 @@ public class Window
String cp = System.getProperty("java.class.path"); String cp = System.getProperty("java.class.path");
if (cp != null) if (cp != null)
win.setClasspath(cp.replace(File.pathSeparatorChar, win.setClassPath(cp.replace(File.pathSeparatorChar,
SearchPath.altPathSeparatorChar)); Decompiler.altPathSeparatorChar));
String cls = win.getClass().getName(); String cls = win.getClass().getName();
win.setClass(cls); win.setClass(cls);

Loading…
Cancel
Save