New bytecode interface, no .java.in files anymore

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1247 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 24 years ago
parent a34a837696
commit 3378492cd3
  1. 0
      jode/jode/GlobalOptions.java
  2. 9
      jode/jode/Makefile.am
  3. 102
      jode/jode/decompiler/ClassAnalyzer.java
  4. 4
      jode/jode/decompiler/DeadCodeAnalysis.java
  5. 37
      jode/jode/decompiler/Decompiler.java
  6. 8
      jode/jode/decompiler/FieldAnalyzer.java
  7. 41
      jode/jode/decompiler/ImportHandler.java
  8. 45
      jode/jode/decompiler/LocalVarEntry.java
  9. 79
      jode/jode/decompiler/LocalVariableRangeList.java
  10. 43
      jode/jode/decompiler/LocalVariableTable.java
  11. 14
      jode/jode/decompiler/Main.java
  12. 12
      jode/jode/decompiler/Makefile.am
  13. 287
      jode/jode/decompiler/MethodAnalyzer.java
  14. 326
      jode/jode/decompiler/Opcodes.java
  15. 18
      jode/jode/decompiler/Options.java
  16. 7
      jode/jode/decompiler/ProgressListener.java
  17. 21
      jode/jode/decompiler/Scope.java
  18. 105
      jode/jode/decompiler/TabbedPrintWriter.java
  19. 4
      jode/jode/expr/CheckNullOperator.java
  20. 70
      jode/jode/expr/ConstOperator.java
  21. 6
      jode/jode/expr/Expression.java
  22. 58
      jode/jode/expr/FieldOperator.java
  23. 332
      jode/jode/expr/InvokeOperator.java
  24. 12
      jode/jode/expr/LocalVarOperator.java
  25. 8
      jode/jode/expr/Makefile.am
  26. 13
      jode/jode/expr/Operator.java
  27. 22
      jode/jode/flow/CaseBlock.java
  28. 6
      jode/jode/flow/CatchBlock.java
  29. 28
      jode/jode/flow/ConditionalBlock.java
  30. 2
      jode/jode/flow/CreateCheckNull.java
  31. 7
      jode/jode/flow/CreateClassField.java
  32. 624
      jode/jode/flow/FlowBlock.java
  33. 6
      jode/jode/flow/IfThenElseBlock.java
  34. 8
      jode/jode/flow/InstructionBlock.java
  35. 9
      jode/jode/flow/InstructionContainer.java
  36. 19
      jode/jode/flow/JsrBlock.java
  37. 6
      jode/jode/flow/LoopBlock.java
  38. 8
      jode/jode/flow/Makefile.am
  39. 6
      jode/jode/flow/RetBlock.java
  40. 6
      jode/jode/flow/ReturnBlock.java
  41. 6
      jode/jode/flow/SequentialBlock.java
  42. 25
      jode/jode/flow/SlotSet.java
  43. 3
      jode/jode/flow/SpecialBlock.java
  44. 39
      jode/jode/flow/StructuredBlock.java
  45. 121
      jode/jode/flow/SwitchBlock.java
  46. 13
      jode/jode/flow/SynchronizedBlock.java
  47. 58
      jode/jode/flow/TransformConstructors.java
  48. 242
      jode/jode/flow/TransformExceptionHandlers.java
  49. 2
      jode/jode/flow/TryBlock.java
  50. 42
      jode/jode/flow/VariableSet.java
  51. 50
      jode/jode/obfuscator/ClassBundle.java
  52. 224
      jode/jode/obfuscator/ClassIdentifier.java
  53. 6
      jode/jode/obfuscator/ConstantRuntimeEnvironment.java
  54. 12
      jode/jode/obfuscator/FieldIdentifier.java
  55. 6
      jode/jode/obfuscator/Identifier.java
  56. 6
      jode/jode/obfuscator/LocalIdentifier.java
  57. 8
      jode/jode/obfuscator/Main.java
  58. 8
      jode/jode/obfuscator/Makefile.am
  59. 8
      jode/jode/obfuscator/MethodIdentifier.java
  60. 4
      jode/jode/obfuscator/OptionHandler.java
  61. 189
      jode/jode/obfuscator/PackageIdentifier.java
  62. 4
      jode/jode/obfuscator/Renamer.java
  63. 6
      jode/jode/obfuscator/ScriptParser.java
  64. 34
      jode/jode/obfuscator/TranslationTable.java
  65. 20
      jode/jode/obfuscator/modules/ConstantAnalyzer.java
  66. 10
      jode/jode/obfuscator/modules/KeywordRenamer.java
  67. 6
      jode/jode/obfuscator/modules/LocalOptimizer.java
  68. 0
      jode/jode/obfuscator/modules/LocalizeFieldTransformer.java
  69. 8
      jode/jode/obfuscator/modules/Makefile.am
  70. 8
      jode/jode/obfuscator/modules/ModifierMatcher.java
  71. 4
      jode/jode/obfuscator/modules/MultiIdentifierMatcher.java
  72. 16
      jode/jode/obfuscator/modules/NameSwapper.java
  73. 4
      jode/jode/obfuscator/modules/RemovePopAnalyzer.java
  74. 4
      jode/jode/obfuscator/modules/SerializePreserver.java
  75. 10
      jode/jode/obfuscator/modules/SimpleAnalyzer.java
  76. 10
      jode/jode/obfuscator/modules/StrongRenamer.java
  77. 8
      jode/jode/obfuscator/modules/UniqueRenamer.java
  78. 4
      jode/jode/obfuscator/modules/WildCard.java
  79. 40
      jode/jode/swingui/HierarchyTreeModel.java
  80. 38
      jode/jode/swingui/Main.java
  81. 8
      jode/jode/swingui/Makefile.am
  82. 43
      jode/jode/swingui/PackagesTreeModel.java
  83. 10
      jode/jode/type/ArrayType.java
  84. 112
      jode/jode/type/ClassInterfacesType.java
  85. 8
      jode/jode/type/Makefile.am
  86. 40
      jode/jode/type/ReferenceType.java
  87. 5
      jode/jode/type/Type.java
  88. 9
      jode/jode/util/Makefile.am
  89. 10
      jode/jode/util/SimpleMap.java
  90. 6
      jode/jode/util/SimpleSet.java
  91. 82
      jode/jode/util/StringQuoter.java
  92. 86
      jode/jode/util/UnifyHash.java

@ -2,11 +2,9 @@
SUBDIRS = util bytecode type jvm expr flow decompiler obfuscator @SWINGUI@ SUBDIRS = util bytecode type jvm expr flow decompiler obfuscator @SWINGUI@
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@
@ -19,7 +17,6 @@ MY_JAVA_FILES = \
noinst_DATA = $(MY_JAVA_FILES:.java=.class) noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES) EXTRA_DIST = $(MY_JAVA_FILES)
JARFILE = jode-@VERSION@.jar JARFILE = jode-@VERSION@.jar
#data_DATA = $(JARFILE) #data_DATA = $(JARFILE)

@ -21,11 +21,10 @@ package jode.decompiler;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.type.MethodType; import jode.type.MethodType;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassFormatException;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.FieldInfo; import jode.bytecode.FieldInfo;
import jode.bytecode.MethodInfo; import jode.bytecode.MethodInfo;
import jode.bytecode.InnerClassInfo;
import jode.bytecode.ConstantPool;
import jode.expr.Expression; import jode.expr.Expression;
import jode.expr.ThisOperator; import jode.expr.ThisOperator;
import jode.flow.TransformConstructors; import jode.flow.TransformConstructors;
@ -38,8 +37,10 @@ import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;
import java.io.IOException; import java.io.IOException;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collection;
import java.util.Set;
///#enddef
public class ClassAnalyzer public class ClassAnalyzer
implements Scope, Declarable, ClassDeclarer implements Scope, Declarable, ClassDeclarer
@ -75,49 +76,67 @@ public class ClassAnalyzer
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
ClassInfo clazz, ImportHandler imports, ClassInfo clazz, ImportHandler imports,
Expression[] outerValues) Expression[] outerValues)
throws ClassFormatException, IOException
{ {
clazz.loadInfo(clazz.MOSTINFO); clazz.load(ClassInfo.ALL);
ClassInfo superClass = clazz.getSuperclass();
String myPackage = clazz.getName().substring
(clazz.getName().lastIndexOf('.') + 1);
while (superClass != null) {
int howMuch = (superClass.getName().startsWith(myPackage)
&& (superClass.getName().lastIndexOf('.')
< myPackage.length()))
? ClassInfo.DECLARATIONS : ClassInfo.PUBLICDECLARATIONS;
try {
superClass.load(howMuch);
} catch (IOException ex) {
GlobalOptions.err.println
("Warning: Can't get "
+ (howMuch == ClassInfo.PUBLICDECLARATIONS
? "public" : "all")
+ " information of " + superClass
+" to detect name conflicts.");
ex.printStackTrace(GlobalOptions.err);
superClass.guess(howMuch);
}
superClass = superClass.getSuperclass();
}
this.parent = parent; this.parent = parent;
this.clazz = clazz; this.clazz = clazz;
this.imports = imports; this.imports = imports;
if (outerValues != null) if (outerValues != null)
this.outerValues = new OuterValues(this, outerValues); this.outerValues = new OuterValues(this, outerValues);
modifiers = clazz.getModifiers(); modifiers = clazz.getModifiers();
name = clazz.getClassName();
if (parent != null) { if (parent != null) {
InnerClassInfo[] outerInfos = clazz.getOuterClasses(); ClassInfo outerClazz = clazz.getOuterClass();
if (outerInfos[0].outer == null || outerInfos[0].name == null) { if (outerClazz == null) {
if (parent instanceof ClassAnalyzer) if (parent instanceof ClassAnalyzer)
throw new jode.AssertError throw new jode.AssertError
("ClassInfo Attributes are inconsistent: " ("ClassInfo Attributes are inconsistent: "
+ clazz.getName()); + clazz.getName()+" parent: "+parent);
} else { } else {
if (!(parent instanceof ClassAnalyzer) if (!(parent instanceof ClassAnalyzer)
|| !(((ClassAnalyzer) parent).clazz.getName() || ((ClassAnalyzer) parent).clazz != outerClazz)
.equals(outerInfos[0].outer))
|| outerInfos[0].name == null)
throw new jode.AssertError throw new jode.AssertError
("ClassInfo Attributes are inconsistent: " ("ClassInfo Attributes are inconsistent: "
+ clazz.getName()); + clazz.getName()+" parent: "+parent);
} }
name = outerInfos[0].name;
modifiers = outerInfos[0].modifiers;
} else {
name = clazz.getName();
int dot = name.lastIndexOf('.');
if (dot >= 0)
name = name.substring(dot+1);
} }
} }
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
ClassInfo clazz, ImportHandler imports) ClassInfo clazz, ImportHandler imports)
throws ClassFormatException, IOException
{ {
this(parent, clazz, imports, null); this(parent, clazz, imports, null);
} }
public ClassAnalyzer(ClassInfo clazz, ImportHandler imports) public ClassAnalyzer(ClassInfo clazz, ImportHandler imports)
throws ClassFormatException, IOException
{ {
this(null, clazz, imports); this(null, clazz, imports);
} }
@ -187,7 +206,7 @@ public class ClassAnalyzer
public void initialize() { public void initialize() {
FieldInfo[] finfos = clazz.getFields(); FieldInfo[] finfos = clazz.getFields();
MethodInfo[] minfos = clazz.getMethods(); MethodInfo[] minfos = clazz.getMethods();
InnerClassInfo[] innerInfos = clazz.getInnerClasses(); ClassInfo[] innerInfos = clazz.getClasses();
if (finfos == null) { if (finfos == null) {
/* This means that the class could not be loaded. /* This means that the class could not be loaded.
@ -206,11 +225,20 @@ public class ClassAnalyzer
int innerCount = innerInfos.length; int innerCount = innerInfos.length;
inners = new ClassAnalyzer[innerCount]; inners = new ClassAnalyzer[innerCount];
for (int i=0; i < innerCount; i++) { for (int i=0; i < innerCount; i++) {
ClassInfo ci = ClassInfo.forName(innerInfos[i].inner); try {
inners[i] = new ClassAnalyzer inners[i] = new ClassAnalyzer
(this, ci, imports, (this, innerInfos[i], imports,
Modifier.isStatic(innerInfos[i].modifiers) Modifier.isStatic(innerInfos[i].getModifiers())
? null : outerThis); ? null : outerThis);
} catch (ClassFormatException ex) {
GlobalOptions.err.println("Inner class "+innerInfos[i]
+" malformed!");
ex.printStackTrace(GlobalOptions.err);
} catch (IOException ex) {
GlobalOptions.err.println("Can't read inner class "
+innerInfos[i]+".");
ex.printStackTrace(GlobalOptions.err);
}
} }
} else } else
inners = new ClassAnalyzer[0]; inners = new ClassAnalyzer[0];
@ -240,6 +268,8 @@ 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++) {
if (inners[j] == null)
continue;
inners[j].initialize(); inners[j].initialize();
innerComplexity += inners[j].getComplexity(); innerComplexity += inners[j].getComplexity();
} }
@ -350,6 +380,8 @@ 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++) {
if (inners[j] == null)
continue;
if (pl != null) { if (pl != null) {
double innerCompl = inners[j].getComplexity() * subScale; double innerCompl = inners[j].getComplexity() * subScale;
if (innerCompl > STEP_COMPLEXITY) { if (innerCompl > STEP_COMPLEXITY) {
@ -393,7 +425,8 @@ public class ClassAnalyzer
for (int j=0; j < fields.length; j++) for (int j=0; j < fields.length; j++)
fields[j].makeDeclaration(done); fields[j].makeDeclaration(done);
for (int j=0; j < inners.length; j++) for (int j=0; j < inners.length; j++)
inners[j].makeDeclaration(done); if (inners[j] != null)
inners[j].makeDeclaration(done);
for (int j=0; j < methods.length; j++) for (int j=0; j < methods.length; j++)
methods[j].makeDeclaration(done); methods[j].makeDeclaration(done);
} }
@ -457,6 +490,11 @@ public class ClassAnalyzer
if (needNewLine) if (needNewLine)
writer.println(); writer.println();
if (inners[i] == null) {
writer.println("COULDN'T READ INNER CLASS!");
continue;
}
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(null, 0.0, 0.0); inners[i].analyze(null, 0.0, 0.0);
@ -542,7 +580,7 @@ public class ClassAnalyzer
writer.print(name); writer.print(name);
ClassInfo superClazz = clazz.getSuperclass(); ClassInfo superClazz = clazz.getSuperclass();
if (superClazz != null && if (superClazz != null &&
superClazz != ClassInfo.javaLangObject) { superClazz.getName() != "java.lang.Object") {
writer.breakOp(); writer.breakOp();
writer.print(" extends " + (writer.getClassString writer.print(" extends " + (writer.getClassString
(superClazz, Scope.CLASSNAME))); (superClazz, Scope.CLASSNAME)));
@ -573,7 +611,7 @@ public class ClassAnalyzer
writer.closeBraceNoSpace(); writer.closeBraceNoSpace();
} else } else
writer.closeBrace(); writer.closeBrace();
clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS); clazz.drop(clazz.DECLARATIONS);
} }
public void dumpJavaFile(TabbedPrintWriter writer) public void dumpJavaFile(TabbedPrintWriter writer)
@ -628,10 +666,10 @@ public class ClassAnalyzer
} }
} }
if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) { if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) {
InnerClassInfo[] iinfos = info.getInnerClasses(); ClassInfo[] iinfos = info.getClasses();
if (iinfos != null) { if (iinfos != null) {
for (int i=0; i < iinfos.length; i++) { for (int i=0; i < iinfos.length; i++) {
if (iinfos[i].name.equals(name)) if (iinfos[i].getClassName().equals(name))
return true; return true;
} }
} }
@ -671,7 +709,7 @@ public class ClassAnalyzer
/** require name != null; **/ /** require name != null; **/
int innerCount = inners.length; int innerCount = inners.length;
for (int i=0; i < innerCount; i++) { for (int i=0; i < innerCount; i++) {
if (inners[i].name.equals(name)) if (inners[i] != null && inners[i].name.equals(name))
return inners[i]; return inners[i];
} }
return null; return null;

@ -22,7 +22,9 @@ import jode.bytecode.BytecodeInfo;
import jode.bytecode.Instruction; import jode.bytecode.Instruction;
import jode.bytecode.Handler; import jode.bytecode.Handler;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
public class DeadCodeAnalysis { public class DeadCodeAnalysis {

@ -19,7 +19,7 @@
package jode.decompiler; package jode.decompiler;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.SearchPath; import jode.bytecode.ClassPath;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import java.io.File; import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -38,7 +38,7 @@ import java.io.BufferedWriter;
* @version 1.0 * @version 1.0
*/ */
public class Decompiler { public class Decompiler {
private SearchPath searchPath = null; private ClassPath classPath = null;
private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT; private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;
@ -51,7 +51,7 @@ public class Decompiler {
* by context. * by context.
*/ */
public static final char altPathSeparatorChar public static final char altPathSeparatorChar
= SearchPath.altPathSeparatorChar; = ClassPath.altPathSeparatorChar;
/** /**
* Create a new decompiler. * Create a new decompiler.
@ -67,7 +67,7 @@ public class Decompiler {
* @see #setClassPath(String[]) * @see #setClassPath(String[])
*/ */
public void setClassPath(String classpath) { public void setClassPath(String classpath) {
searchPath = new SearchPath(classpath); this.classPath = new ClassPath(classpath);
} }
/** /**
@ -80,10 +80,19 @@ public class Decompiler {
* @see #setClassPath(String) * @see #setClassPath(String)
*/ */
public void setClassPath(String[] classpath) { public void setClassPath(String[] classpath) {
StringBuffer sb = new StringBuffer(classpath[0]); this.classPath = new ClassPath(classpath);
for (int i = 1; i < classpath.length; i++) }
sb.append(altPathSeparatorChar).append(classpath[i]);
searchPath = new SearchPath(sb.toString()); /**
* Set the class path. Should be called once before decompile is
* called, otherwise the system class path is used.
* @param classpath a classpath object.
* @exception NullPointerException if classpath is null.
* @exception IndexOutOfBoundsException if classpath array is empty.
* @see #setClassPath(String)
*/
public void setClassPath(ClassPath classpath) {
this.classPath = classpath;
} }
private static final String[] optionStrings = { private static final String[] optionStrings = {
@ -174,14 +183,16 @@ public class Decompiler {
public void decompile(String className, Writer writer, public void decompile(String className, Writer writer,
ProgressListener progress) ProgressListener progress)
throws java.io.IOException { throws java.io.IOException {
if (searchPath == null) { if (classPath == null) {
String classPath = System.getProperty("java.class.path") String cp = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, altPathSeparatorChar); .replace(File.pathSeparatorChar, altPathSeparatorChar);
searchPath = new SearchPath(classPath); classPath = new ClassPath(cp);
} }
ClassInfo.setClassPath(searchPath); /* XXX, comment the next line, as soon as ClassInfo.forName is
ClassInfo clazz = ClassInfo.forName(className); * no longer used. */
ClassInfo.setClassPath(classPath);
ClassInfo clazz = classPath.getClassInfo(className);
ImportHandler imports = new ImportHandler(importPackageLimit, ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit); importClassLimit);
TabbedPrintWriter tabbedWriter = TabbedPrintWriter tabbedWriter =

@ -29,7 +29,9 @@ import jode.expr.OuterLocalOperator;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.io.IOException; import java.io.IOException;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
public class FieldAnalyzer implements Analyzer { public class FieldAnalyzer implements Analyzer {
ClassAnalyzer clazz; ClassAnalyzer clazz;
@ -191,8 +193,8 @@ public class FieldAnalyzer implements Analyzer {
== (Modifier.STATIC | Modifier.FINAL)) { == (Modifier.STATIC | Modifier.FINAL)) {
/* Static final fields must always be initialized */ /* Static final fields must always be initialized */
writer.breakOp(); writer.breakOp();
writer.print(" = null"); writer.print(" = null;");
} }
writer.endOp(); writer.endOp();
writer.println(";"); writer.println(";");
} }

@ -20,18 +20,19 @@
package jode.decompiler; package jode.decompiler;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
import jode.type.Type; import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.type.ClassInterfacesType; import jode.type.ClassInterfacesType;
import jode.type.NullType; import jode.type.NullType;
import @COLLECTIONS@.SortedMap; ///#def COLLECTIONS java.util
import @COLLECTIONS@.TreeMap; import java.util.SortedMap;
import @COLLECTIONS@.List; import java.util.TreeMap;
import @COLLECTIONS@.LinkedList; import java.util.List;
import @COLLECTIONS@.Comparator; import java.util.LinkedList;
import @COLLECTIONS@.Iterator; import java.util.Comparator;
import java.util.Iterator;
///#enddef
import java.io.IOException; import java.io.IOException;
import java.util.Hashtable; import java.util.Hashtable;
@ -252,17 +253,25 @@ public class ImportHandler {
*/ */
public void useClass(ClassInfo clazz) { public void useClass(ClassInfo clazz) {
for (;;) { for (;;) {
/* First handle inner classes: For class scoped classes try {
* import outer class instead; for method scoped classes /* First handle inner classes: For class scoped classes
* we don't import anything. * import outer class instead; for method scoped classes
*/ * we don't import anything.
InnerClassInfo[] outerInfo = clazz.getOuterClasses(); */
if (outerInfo == null) clazz.load(ClassInfo.OUTERCLASS);
} catch (IOException ex) {
/* If we can't load outer class information, assume
* the clazz is not method or class scoped in this
* class. There is a big error otherwise anyways.
*/
break; break;
}
if (outerInfo[0].name == null || outerInfo[0].outer == null) if (clazz.isMethodScoped())
return; return;
clazz = ClassInfo.forName(outerInfo[0].outer); ClassInfo outer = clazz.getOuterClass();
if (outer == null)
break;
clazz = outer;
} }
String name = clazz.getName(); String name = clazz.getName();

@ -1,45 +0,0 @@
/* LocalVarEntry Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.decompiler;
import jode.type.Type;
public class LocalVarEntry {
String name;
Type type;
int startAddr;
int endAddr;
LocalVarEntry next;
public LocalVarEntry(int s, int e, String n, Type t) {
startAddr = s;
endAddr = e;
name = n;
type = t;
next = null;
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
}

@ -1,79 +0,0 @@
/* LocalVariableRangeList 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.GlobalOptions;
import jode.type.Type;
public class LocalVariableRangeList {
LocalVarEntry list = null;
LocalVariableRangeList() {
}
private void add(LocalVarEntry li) {
LocalVarEntry prev = null;
LocalVarEntry next = list;
while (next != null && next.endAddr < li.startAddr) {
prev = next;
next = next.next;
}
/* prev.endAddr < li.startAddr <= next.endAddr
*/
if (next != null && li.endAddr >= next.startAddr) {
if (next.type.equals(li.type)
&& next.name.equals(li.name)) {
/* Same type, same name and overlapping range.
* This is the same local: extend next to the common
* range and don't add li.
*/
next.startAddr = Math.min(next.startAddr, li.startAddr);
next.endAddr = Math.max(next.endAddr, li.endAddr);
return;
}
GlobalOptions.err.println("warning: non disjoint locals");
}
li.next = next;
if (prev == null)
list = li;
else
prev.next = li;
}
private LocalVarEntry find(int addr) {
LocalVarEntry li = list;
while (li != null && li.endAddr < addr)
li = li.next;
if (li == null || li.startAddr > addr) {
return null;
}
return li;
}
public void addLocal(int startAddr, int endAddr,
String name, Type type) {
LocalVarEntry li = new LocalVarEntry(startAddr,endAddr,name,type);
add (li);
}
public LocalVarEntry getInfo(int addr) {
return find(addr);
}
}

@ -1,43 +0,0 @@
/* LocalVariableTable 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.type.Type;
import jode.bytecode.LocalVariableInfo;
public class LocalVariableTable {
LocalVariableRangeList[] locals;
public LocalVariableTable(int maxLocals, LocalVariableInfo[] lvt) {
locals = new LocalVariableRangeList[maxLocals];
for (int i=0; i < maxLocals; i++)
locals[i] = new LocalVariableRangeList();
for (int i=0; i<lvt.length; i++)
locals[lvt[i].slot].addLocal(lvt[i].start.getAddr(),
lvt[i].end.getAddr(),
lvt[i].name, Type.tType(lvt[i].type));
}
public LocalVarEntry getLocal(int slot, int addr)
throws ArrayIndexOutOfBoundsException
{
return locals[slot].getInfo(addr);
}
}

@ -19,7 +19,7 @@
package jode.decompiler; package jode.decompiler;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.SearchPath; import jode.bytecode.ClassPath;
import jode.GlobalOptions; import jode.GlobalOptions;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -144,8 +144,9 @@ public class Main extends Options {
return; return;
} }
String classPath = System.getProperty("java.class.path") ClassPath classPath;
.replace(File.pathSeparatorChar, SearchPath.altPathSeparatorChar); String classPathStr = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, ClassPath.altPathSeparatorChar);
String destDir = null; String destDir = null;
int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
@ -168,7 +169,7 @@ public class Main extends Options {
GlobalOptions.err.println(GlobalOptions.version); GlobalOptions.err.println(GlobalOptions.version);
break; break;
case 'c': case 'c':
classPath = g.getOptarg(); classPathStr = g.getOptarg();
break; break;
case 'd': case 'd':
destDir = g.getOptarg(); destDir = g.getOptarg();
@ -246,7 +247,8 @@ public class Main extends Options {
} }
if (errorInParams) if (errorInParams)
return; return;
ClassInfo.setClassPath(classPath.toString()); classPath = new ClassPath(classPathStr);
ClassInfo.setClassPath(classPath);
ImportHandler imports = new ImportHandler(importPackageLimit, ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit); importClassLimit);
@ -270,7 +272,7 @@ public class Main extends Options {
try { try {
ClassInfo clazz; ClassInfo clazz;
try { try {
clazz = ClassInfo.forName(params[i]); clazz = classPath.getClassInfo(params[i]);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("`"+params[i]+"' is not a class name"); ("`"+params[i]+"' is not a class name");

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@
@ -15,15 +13,11 @@ MY_JAVA_FILES = \
Applet.java \ Applet.java \
ClassAnalyzer.java \ ClassAnalyzer.java \
ClassDeclarer.java \ ClassDeclarer.java \
DeadCodeAnalysis.java \
Declarable.java \ Declarable.java \
Decompiler.java \ Decompiler.java \
FieldAnalyzer.java \ FieldAnalyzer.java \
ImportHandler.java \ ImportHandler.java \
LocalInfo.java \ LocalInfo.java \
LocalVarEntry.java \
LocalVariableRangeList.java \
LocalVariableTable.java \
Main.java \ Main.java \
MethodAnalyzer.java \ MethodAnalyzer.java \
Opcodes.java \ Opcodes.java \

@ -20,12 +20,13 @@
package jode.decompiler; package jode.decompiler;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.BytecodeInfo; import jode.bytecode.BasicBlocks;
import jode.bytecode.Block;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.MethodInfo;
import jode.bytecode.Handler; import jode.bytecode.Handler;
import jode.bytecode.Instruction; import jode.bytecode.Instruction;
import jode.bytecode.LocalVariableInfo; import jode.bytecode.LocalVariableInfo;
import jode.bytecode.MethodInfo;
import jode.jvm.SyntheticAnalyzer; import jode.jvm.SyntheticAnalyzer;
import jode.type.*; import jode.type.*;
import jode.expr.Expression; import jode.expr.Expression;
@ -52,11 +53,13 @@ import java.io.DataInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Collection; import java.util.Map;
import @COLLECTIONS@.ArrayList; import java.util.Collection;
import @COLLECTIONS@.Iterator; import java.util.ArrayList;
import @COLLECTIONS@.Set; import java.util.Iterator;
import java.util.Set;
///#enddef
/** /**
* A method analyzer is the main class for analyzation of methods. * A method analyzer is the main class for analyzation of methods.
@ -93,10 +96,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
*/ */
MethodInfo minfo; MethodInfo minfo;
/** /**
* This is the bytecode info structure, or null if this method has * This is the basic blocks structure, or null if this method has
* no code (abstract or native). * no code (abstract or native).
*/ */
BytecodeInfo code; BasicBlocks bb;
/** /**
* The method name. * The method name.
@ -139,11 +142,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* the implicit <i>this</i> parameter for nonstatic methods. * the implicit <i>this</i> parameter for nonstatic methods.
*/ */
LocalInfo[] param; LocalInfo[] param;
/**
* The local variable table containing info about names and types of
* locals.
*/
LocalVariableTable lvt;
/** /**
* If this method is the special constructor, that is generated * If this method is the special constructor, that is generated
@ -201,9 +199,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
this.isConstructor = this.isConstructor =
methodName.equals("<init>") || methodName.equals("<clinit>"); methodName.equals("<init>") || methodName.equals("<clinit>");
if (minfo.getBytecode() != null) { if (minfo.getBasicBlocks() != null)
code = minfo.getBytecode(); bb = minfo.getBasicBlocks();
}
String[] excattr = minfo.getExceptions(); String[] excattr = minfo.getExceptions();
if (excattr == null) { if (excattr == null) {
exceptions = new Type[0]; exceptions = new Type[0];
@ -214,7 +212,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
exceptions[i] = Type.tClass(excattr[i]); exceptions[i] = Type.tClass(excattr[i]);
} }
if (minfo.isSynthetic() || methodName.indexOf('$') != -1) if (minfo.isSynthetic() || methodName.indexOf('$') != -1)
synth = new SyntheticAnalyzer(minfo, true); synth = new SyntheticAnalyzer(cla.getClazz(), minfo, true);
} }
/** /**
@ -245,8 +243,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* @return the bytecode info for this method, or null if it is * @return the bytecode info for this method, or null if it is
* abstract or native. * abstract or native.
*/ */
public final BytecodeInfo getBytecodeInfo() { public final BasicBlocks getBasicBlocks() {
return code; return bb;
} }
/** /**
@ -279,12 +277,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
*/ */
public void insertStructuredBlock(StructuredBlock insertBlock) { public void insertStructuredBlock(StructuredBlock insertBlock) {
if (methodHeader != null) { if (methodHeader != null) {
insertBlock.setJump(new Jump(FlowBlock.NEXT_BY_ADDR)); methodHeader.prependBlock(insertBlock);
FlowBlock insertFlowBlock = new FlowBlock(this, 0);
insertFlowBlock.appendBlock(insertBlock, 0);
insertFlowBlock.setNextByAddr(methodHeader);
insertFlowBlock.doT2(methodHeader);
methodHeader = insertFlowBlock;
} else { } else {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@ -415,13 +408,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* @param slot the slot, the local variable uses. * @param slot the slot, the local variable uses.
* @return a new local info representing that local. * @return a new local info representing that local.
*/ */
public LocalInfo getLocalInfo(int addr, int slot) { public LocalInfo getLocalInfo(LocalVariableInfo lvi) {
LocalInfo li = new LocalInfo(this, slot); LocalInfo li = new LocalInfo(this, lvi.getSlot());
if (lvt != null) { if (lvi.getName() != null)
LocalVarEntry entry = lvt.getLocal(slot, addr); li.addHint(lvi.getName(), Type.tType(lvi.getType()));
if (entry != null)
li.addHint(entry.getName(), entry.getType());
}
allLocals.addElement(li); allLocals.addElement(li);
return li; return li;
} }
@ -431,10 +421,15 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* been initialized. This is used for a nice progress bar. * been initialized. This is used for a nice progress bar.
*/ */
public double getComplexity() { public double getComplexity() {
if (code == null) if (bb == null)
return 0.0; return 0.0;
else else {
return code.getInstructions().size(); int count = 0;
Block[] blocks = bb.getBlocks();
for (int i=0; i < blocks.length; i++)
count += blocks[i].getInstructions().size();
return (double) count;
}
} }
/** /**
@ -444,118 +439,76 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
private void analyzeCode(ProgressListener pl, double done, double scale) private void analyzeCode(ProgressListener pl, double done, double scale)
{ {
int instrsPerStep = Integer.MAX_VALUE; int instrsPerStep = Integer.MAX_VALUE;
double instrScale = (scale * 0.9) / getComplexity();
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print(methodName+": "); GlobalOptions.err.print(methodName+": ");
if (pl != null) { if (pl != null)
instrsPerStep = (int) ((code.getInstructions().size() instrsPerStep = (int) (STEP_COMPLEXITY / instrScale);
* STEP_COMPLEXITY) / (scale * 0.9));
}
/* The adjacent analyzation relies on this */ Block[] blocks = bb.getBlocks();
DeadCodeAnalysis.removeDeadCode(code); FlowBlock[] flows = new FlowBlock[blocks.length];
Handler[] handlers = code.getExceptionHandlers();
int returnCount; int returnCount;
TransformExceptionHandlers excHandlers; TransformExceptionHandlers excHandlers;
{ {
/* First create a FlowBlock for every block that has a for (int i=0; i < blocks.length; i++)
* predecessor other than the previous instruction. flows[i] = new FlowBlock(this, i, i > 0 ? flows[i-1]: null);
*/
for (Iterator i = code.getInstructions().iterator();
i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
if (instr.getPrevByAddr() == null
|| instr.getPrevByAddr().doesAlwaysJump()
|| instr.getPreds() != null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
}
for (int i=0; i < handlers.length; i++) {
Instruction instr = handlers[i].start;
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
/* end doesn't have a predecessor, but we must prevent
* it from being merged with the previous instructions.
*/
instr = handlers[i].end.getNextByAddr();
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
instr = handlers[i].catcher;
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
}
/* While we read the opcodes into FlowBlocks /* While we read the opcodes into FlowBlocks
* we try to combine sequential blocks, as soon as we * we try to combine sequential blocks, as soon as we
* find two sequential instructions in a row, where the * find two sequential instructions in a row, where the
* second has no predecessors. * second has no predecessors.
*/ */
int mark = 1000;
int count = 0; int count = 0;
FlowBlock lastBlock = null; for (int i=0; i < blocks.length; i++) {
boolean lastSequential = false; int mark = 100;
for (Iterator i = code.getInstructions().iterator(); int last = blocks[i].getInstructions().size() - 1;
i.hasNext(); ) { for (int j=0; j <= last; j++) {
Instruction instr = (Instruction) i.next(); Instruction instr
= (Instruction) blocks[i].getInstructions().get(j);
jode.flow.StructuredBlock block if (GlobalOptions.verboseLevel > 0 && j > mark) {
= Opcodes.readOpcode(instr, this); GlobalOptions.err.print('.');
mark += 100;
if (GlobalOptions.verboseLevel > 0 && instr.getAddr() > mark) { }
GlobalOptions.err.print('.'); if (++count >= instrsPerStep) {
mark += 1000; done += count * instrScale;
} pl.updateProgress(done, methodName);
if (++count >= instrsPerStep) { count = 0;
done += count * scale / code.getInstructions().size(); }
pl.updateProgress(done, methodName); Opcodes.addOpcode(flows[i], instr, this);
count = 0;
} }
Block[] succs = blocks[i].getSuccs();
FlowBlock[] flowSuccs = new FlowBlock[succs.length];
if (lastSequential && instr.getTmpInfo() == null for (int j=0; j< succs.length; j++) {
/* Only merge with previous block, if this is sequential, if (succs[j] == null)
* too. flowSuccs[j] = FlowBlock.END_OF_METHOD;
* Why? appendBlock only handles sequential blocks. else
*/ flowSuccs[j] = flows[succs[j].getBlockNr()];
&& !instr.doesAlwaysJump() && instr.getSuccs() == null) { }
flows[i].setSuccessors(flowSuccs);
lastBlock.appendBlock(block, instr.getLength());
} else {
if (instr.getTmpInfo() == null)
instr.setTmpInfo(new FlowBlock(this, instr.getAddr()));
FlowBlock flowBlock = (FlowBlock) instr.getTmpInfo();
flowBlock.appendBlock(block, instr.getLength());
if (lastBlock != null)
lastBlock.setNextByAddr(flowBlock);
instr.setTmpInfo(lastBlock = flowBlock);
lastSequential = !instr.doesAlwaysJump()
&& instr.getSuccs() == null;
}
} }
methodHeader = (FlowBlock) done += count * instrScale;
((Instruction) code.getInstructions().get(0)).getTmpInfo(); Block startBlock = bb.getStartBlock();
excHandlers = new TransformExceptionHandlers(); if (startBlock == null)
methodHeader = new FlowBlock(this, 0, null);
else
methodHeader = flows[startBlock.getBlockNr()];
Handler[] handlers = bb.getExceptionHandlers();
excHandlers = new TransformExceptionHandlers(flows);
for (int i=0; i<handlers.length; i++) { for (int i=0; i<handlers.length; i++) {
Type type = null; Type type = null;
FlowBlock start FlowBlock start = flows[handlers[i].getStart().getBlockNr()];
= (FlowBlock) handlers[i].start.getTmpInfo(); FlowBlock end = flows[handlers[i].getEnd().getBlockNr()];
int endAddr = handlers[i].end.getNextByAddr().getAddr();
FlowBlock handler FlowBlock handler
= (FlowBlock) handlers[i].catcher.getTmpInfo(); = flows[handlers[i].getCatcher().getBlockNr()];
if (handlers[i].type != null) if (handlers[i].getType() != null)
type = Type.tClass(handlers[i].type); type = Type.tClass(handlers[i].getType());
excHandlers.addHandler(start, endAddr, handler, type); excHandlers.addHandler(start, end, handler, type);
} }
} }
for (Iterator i = code.getInstructions().iterator(); i.hasNext(); ) {
Instruction instr = (Instruction) i.next();
instr.setTmpInfo(null);
}
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.print('-'); GlobalOptions.err.print('-');
@ -588,10 +541,10 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
{ {
if (pl != null) if (pl != null)
pl.updateProgress(done, methodName); pl.updateProgress(done, methodName);
if (code != null) { if (bb != null) {
if ((Options.options & Options.OPTION_VERIFY) != 0) { if ((Options.options & Options.OPTION_VERIFY) != 0) {
CodeVerifier verifier CodeVerifier verifier
= new CodeVerifier(getClazz(), minfo, code); = new CodeVerifier(getClazz(), minfo, bb);
try { try {
verifier.verify(); verifier.verify();
} catch (VerifyException ex) { } catch (VerifyException ex) {
@ -599,13 +552,6 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
throw new jode.AssertError("Verification error"); 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();
@ -616,17 +562,22 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int slot = 0; int slot = 0;
if (!isStatic()) { if (!isStatic()) {
ClassInfo classInfo = classAnalyzer.getClazz(); ClassInfo classInfo = classAnalyzer.getClazz();
LocalInfo thisLocal = getLocalInfo(0, slot++); param[offset] = getLocalInfo(bb != null
thisLocal.setExpression(new ThisOperator(classInfo, true)); ? bb.getParamInfo(slot)
param[offset++] = thisLocal; : LocalVariableInfo.getInfo(slot));
param[offset].setExpression(new ThisOperator(classInfo, true));
slot++;
offset++;
} }
for (int i=0; i< paramTypes.length; i++) { for (int i=0; i< paramTypes.length; i++) {
param[offset] = getLocalInfo(0, slot); param[offset] = getLocalInfo(bb != null
? bb.getParamInfo(slot)
: LocalVariableInfo.getInfo(slot));
param[offset].setType(paramTypes[i]); param[offset].setType(paramTypes[i]);
slot += paramTypes[i].stackSize(); slot += paramTypes[i].stackSize();
offset++; offset++;
} }
for (int i= 0; i< exceptions.length; i++) for (int i= 0; i< exceptions.length; i++)
imports.useType(exceptions[i]); imports.useType(exceptions[i]);
@ -634,7 +585,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (!isConstructor) if (!isConstructor)
imports.useType(methodType.getReturnType()); imports.useType(methodType.getReturnType());
if (code != null) if (bb != null)
analyzeCode(pl, done, scale); analyzeCode(pl, done, scale);
} }
@ -698,7 +649,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
done.add(param[i]); done.add(param[i]);
} }
if (code != null) { if (bb != null) {
methodHeader.makeDeclaration(done); methodHeader.makeDeclaration(done);
methodHeader.simplify(); methodHeader.simplify();
} }
@ -833,25 +784,22 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
modifiedModifiers &= ~Modifier.ABSTRACT; modifiedModifiers &= ~Modifier.ABSTRACT;
/* Don't ask me why, but jikes declares the static constructor /* Don't ask me why, but jikes declares the static constructor
* as final. Another compiler or obfuscator seems to declare * as final.
* it as public. I remove every fancy modifier, now.
*/ */
if (isConstructor() && isStatic()) if (isConstructor() && isStatic())
modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC modifiedModifiers &= ~Modifier.FINAL;
| Modifier.PROTECTED | Modifier.PRIVATE);
writer.startOp(writer.NO_PAREN, 1);
String delim = ""; String delim ="";
if (minfo.isSynthetic()) { if (minfo.isSynthetic()) {
writer.print("/*synthetic*/"); writer.print("/*synthetic*/");
delim = " "; delim = " ";
} }
String modif = Modifier.toString(modifiedModifiers); String modif = Modifier.toString(modifiedModifiers);
if (modif.length() > 0) { writer.print(delim + modif);
writer.print(delim + modif); if (modif.length() > 0)
delim = " "; delim = " ";
}
if (isConstructor if (isConstructor
&& (isStatic() && (isStatic()
|| (classAnalyzer.getName() == null || (classAnalyzer.getName() == null
@ -865,36 +813,25 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.printType(getReturnType()); writer.printType(getReturnType());
writer.print(" " + methodName); writer.print(" " + methodName);
} }
writer.breakOp();
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0); int offset = skipParams + (isStatic() ? 0 : 1);
int offset = skipParams + (isStatic() ? 0 : 1);
for (int i = offset; i < param.length; i++) { for (int i = offset; i < param.length; i++) {
if (i > offset) { if (i > offset)
writer.print(", "); writer.print(", ");
writer.breakOp();
}
param[i].dumpDeclaration(writer); param[i].dumpDeclaration(writer);
} }
writer.endOp();
writer.print(")"); writer.print(")");
} }
if (exceptions.length > 0) { if (exceptions.length > 0) {
writer.breakOp(); writer.println("");
writer.print(" throws "); writer.print("throws ");
writer.startOp(writer.EXPL_PAREN, 2);
for (int i= 0; i< exceptions.length; i++) { for (int i= 0; i< exceptions.length; i++) {
if (i > 0) { if (i > 0)
writer.print(","); writer.print(", ");
writer.breakOp();
writer.print(" ");
}
writer.printType(exceptions[i]); writer.printType(exceptions[i]);
} }
writer.endOp();
} }
writer.endOp(); if (bb != null) {
if (code != null) {
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();
methodHeader.dumpSource(writer); methodHeader.dumpSource(writer);
@ -1031,8 +968,14 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
outerValueArray = newOuter; outerValueArray = newOuter;
break; break;
} }
anonAnalyzer = new ClassAnalyzer(this, clazz, imports, try {
outerValueArray); anonAnalyzer = new ClassAnalyzer(this, clazz, imports,
outerValueArray);
} catch (IOException ex) {
GlobalOptions.err.println
("Error while reading anonymous class "+clazz+".");
return;
}
addClassAnalyzer(anonAnalyzer); addClassAnalyzer(anonAnalyzer);
anonAnalyzer.initialize(); anonAnalyzer.initialize();
anonAnalyzer.analyze(null, 0.0, 0.0); anonAnalyzer.analyze(null, 0.0, 0.0);

@ -76,7 +76,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
Instruction instr, Instruction instr,
Expression expr) Expression expr)
{ {
return new InstructionBlock(expr, new Jump(FlowBlock.NEXT_BY_ADDR)); return new InstructionBlock(expr);
} }
private static StructuredBlock createSpecial(MethodAnalyzer ma, private static StructuredBlock createSpecial(MethodAnalyzer ma,
@ -84,39 +84,33 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
int type, int type,
int stackcount, int param) int stackcount, int param)
{ {
return new SpecialBlock(type, stackcount, param, return new SpecialBlock(type, stackcount, param);
new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createGoto(MethodAnalyzer ma, private static StructuredBlock createGoto(MethodAnalyzer ma,
Instruction instr) Instruction instr)
{ {
return new EmptyBlock return new EmptyBlock();
(new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()));
} }
private static StructuredBlock createJsr(MethodAnalyzer ma, private static StructuredBlock createJsr(MethodAnalyzer ma,
Instruction instr) Instruction instr)
{ {
return new JsrBlock return new JsrBlock();
(new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()),
new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createIfGoto(MethodAnalyzer ma, private static StructuredBlock createIfGoto(MethodAnalyzer ma,
Instruction instr, Instruction instr,
Expression expr) Expression expr)
{ {
return new ConditionalBlock return new ConditionalBlock(expr);
(expr, new Jump((FlowBlock)instr.getSingleSucc().getTmpInfo()),
new Jump(FlowBlock.NEXT_BY_ADDR));
} }
private static StructuredBlock createSwitch(MethodAnalyzer ma, private static StructuredBlock createSwitch(MethodAnalyzer ma,
Instruction instr, Instruction instr,
int[] cases, FlowBlock[] dests) int[] cases)
{ {
return new SwitchBlock(new NopOperator(Type.tUInt), cases, dests); return new SwitchBlock(new NopOperator(Type.tUInt), cases);
} }
private static StructuredBlock createBlock(MethodAnalyzer ma, private static StructuredBlock createBlock(MethodAnalyzer ma,
@ -134,107 +128,129 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
} }
/** /**
* Read an opcode out of a data input stream containing the bytecode. * Converts an instruction to a StructuredBlock and appencs it to the
* @param addr The current address. * flow block.
* @param stream The stream containing the java byte code. * @param flow The flowblock to which we should add.
* @param instr The instruction to add.
* @param ma The Method Analyzer * @param ma The Method Analyzer
* (where further information can be get from). * (where further information can be get from).
* @return The FlowBlock representing this opcode * @return The FlowBlock representing this opcode
* or null if the stream is empty. * or null if the stream is empty.
* @exception IOException if an read error occured.
* @exception ClassFormatError if an invalid opcode is detected.
*/ */
public static StructuredBlock readOpcode(Instruction instr, public static void addOpcode(FlowBlock flow, Instruction instr,
MethodAnalyzer ma) MethodAnalyzer ma)
throws ClassFormatError
{ {
int opcode = instr.getOpcode(); int opcode = instr.getOpcode();
switch (opcode) { switch (opcode) {
case opc_nop: case opc_nop:
return createBlock(ma, instr, new EmptyBlock break;
(new Jump(FlowBlock.NEXT_BY_ADDR)));
case opc_ldc: case opc_ldc:
case opc_ldc2_w: case opc_ldc2_w:
return createNormal (ma, instr, flow.appendBlock
new ConstOperator(instr.getConstant())); (createNormal(ma, instr,
new ConstOperator(instr.getConstant())));
break;
case opc_iload: case opc_lload: case opc_iload: case opc_lload:
case opc_fload: case opc_dload: case opc_aload: case opc_fload: case opc_dload: case opc_aload: {
return createNormal LocalInfo local = ma.getLocalInfo(instr.getLocalInfo());
(ma, instr, new LocalLoadOperator flow.appendReadBlock
(types[LOCAL_TYPES][opcode-opc_iload], ma, (createNormal
ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot()))); (ma, instr, new LocalLoadOperator
(types[LOCAL_TYPES][opcode-opc_iload], ma, local)), local);
break;
}
case opc_iaload: case opc_laload: case opc_iaload: case opc_laload:
case opc_faload: case opc_daload: case opc_aaload: case opc_faload: case opc_daload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload: case opc_baload: case opc_caload: case opc_saload:
return createNormal flow.appendBlock
(ma, instr, new ArrayLoadOperator (createNormal
(types[ARRAY_TYPES][opcode - opc_iaload])); (ma, instr, new ArrayLoadOperator
(types[ARRAY_TYPES][opcode - opc_iaload])));
break;
case opc_istore: case opc_lstore: case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore: case opc_fstore: case opc_dstore: case opc_astore: {
return createNormal LocalInfo local = ma.getLocalInfo(instr.getLocalInfo());
(ma, instr, new StoreInstruction flow.appendWriteBlock
(new LocalStoreOperator (createNormal
(types[LOCAL_TYPES][opcode-opc_istore], (ma, instr, new StoreInstruction
ma.getLocalInfo(instr.getNextByAddr().getAddr(), (new LocalStoreOperator
instr.getLocalSlot())))); (types[LOCAL_TYPES][opcode-opc_istore], local))), local);
break;
}
case opc_iastore: case opc_lastore: case opc_iastore: case opc_lastore:
case opc_fastore: case opc_dastore: case opc_aastore: case opc_fastore: case opc_dastore: case opc_aastore:
case opc_bastore: case opc_castore: case opc_sastore: case opc_bastore: case opc_castore: case opc_sastore:
return createNormal flow.appendBlock
(ma, instr, new StoreInstruction (createNormal
(new ArrayStoreOperator (ma, instr, new StoreInstruction
(types[ARRAY_TYPES][opcode - opc_iastore]))); (new ArrayStoreOperator
(types[ARRAY_TYPES][opcode - opc_iastore]))));
break;
case opc_pop: case opc_pop2: case opc_pop: case opc_pop2:
return createSpecial flow.appendBlock
(ma, instr, SpecialBlock.POP, opcode - opc_pop + 1, 0); (createSpecial
case opc_dup: case opc_dup_x1: case opc_dup_x2: (ma, instr, SpecialBlock.POP, opcode - opc_pop + 1, 0));
break;
case opc_dup: case opc_dup_x1: case opc_dup_x2:
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: case opc_dup2: case opc_dup2_x1: case opc_dup2_x2:
return createSpecial flow.appendBlock
(ma, instr, SpecialBlock.DUP, (createSpecial
(opcode - opc_dup)/3+1, (opcode - opc_dup)%3); (ma, instr, SpecialBlock.DUP,
(opcode - opc_dup)/3+1, (opcode - opc_dup)%3));
break;
case opc_swap: case opc_swap:
return createSpecial(ma, instr, SpecialBlock.SWAP, 1, 0); flow.appendBlock
(createSpecial(ma, instr, SpecialBlock.SWAP, 1, 0));
break;
case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd:
case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub:
case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul:
case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv:
case opc_irem: case opc_lrem: case opc_frem: case opc_drem: case opc_irem: case opc_lrem: case opc_frem: case opc_drem:
return createNormal flow.appendBlock
(ma, instr, new BinaryOperator (createNormal
(types[BIN_TYPES][(opcode - opc_iadd)%4], (ma, instr, new BinaryOperator
(opcode - opc_iadd)/4+Operator.ADD_OP)); (types[BIN_TYPES][(opcode - opc_iadd)%4],
(opcode - opc_iadd)/4+Operator.ADD_OP)));
break;
case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg:
return createNormal flow.appendBlock
(ma, instr, new UnaryOperator (createNormal
(types[UNARY_TYPES][opcode - opc_ineg], Operator.NEG_OP)); (ma, instr, new UnaryOperator
(types[UNARY_TYPES][opcode - opc_ineg], Operator.NEG_OP)));
break;
case opc_ishl: case opc_lshl: case opc_ishl: case opc_lshl:
case opc_ishr: case opc_lshr: case opc_ishr: case opc_lshr:
case opc_iushr: case opc_lushr: case opc_iushr: case opc_lushr:
return createNormal flow.appendBlock
(ma, instr, new ShiftOperator (createNormal
(types[UNARY_TYPES][(opcode - opc_ishl)%2], (ma, instr, new ShiftOperator
(opcode - opc_ishl)/2 + Operator.SHIFT_OP)); (types[UNARY_TYPES][(opcode - opc_ishl)%2],
(opcode - opc_ishl)/2 + Operator.SHIFT_OP)));
break;
case opc_iand: case opc_land: case opc_iand: case opc_land:
case opc_ior : case opc_lor : case opc_ior : case opc_lor :
case opc_ixor: case opc_lxor: case opc_ixor: case opc_lxor:
return createNormal flow.appendBlock
(ma, instr, new BinaryOperator (createNormal
(types[ZBIN_TYPES][(opcode - opc_iand)%2], (ma, instr, new BinaryOperator
(opcode - opc_iand)/2 + Operator.AND_OP)); (types[ZBIN_TYPES][(opcode - opc_iand)%2],
(opcode - opc_iand)/2 + Operator.AND_OP)));
break;
case opc_iinc: { case opc_iinc: {
LocalInfo local = ma.getLocalInfo(instr.getLocalInfo());
int value = instr.getIncrement(); int value = instr.getIncrement();
int operation = Operator.ADD_OP; int operation = Operator.ADD_OP;
if (value < 0) { if (value < 0) {
value = -value; value = -value;
operation = Operator.SUB_OP; operation = Operator.SUB_OP;
} }
LocalInfo li flow.appendReadBlock
= ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot()); (createNormal
return createNormal (ma, instr, new IIncOperator
(ma, instr, new IIncOperator (new LocalStoreOperator(Type.tInt, local),
(new LocalStoreOperator(Type.tInt, li), value, operation + Operator.OPASSIGN_OP)), local);
value, operation + Operator.OPASSIGN_OP)); break;
} }
case opc_i2l: case opc_i2f: case opc_i2d: case opc_i2l: case opc_i2f: case opc_i2d:
case opc_l2i: case opc_l2f: case opc_l2d: case opc_l2i: case opc_l2f: case opc_l2d:
@ -244,87 +260,102 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
int to = (opcode-opc_i2l)%3; int to = (opcode-opc_i2l)%3;
if (to >= from) if (to >= from)
to++; to++;
return createNormal flow.appendBlock
(ma, instr, new ConvertOperator(types[UNARY_TYPES][from], (createNormal
types[UNARY_TYPES][to])); (ma, instr, new ConvertOperator(types[UNARY_TYPES][from],
types[UNARY_TYPES][to])));
break;
} }
case opc_i2b: case opc_i2c: case opc_i2s: case opc_i2b: case opc_i2c: case opc_i2s:
return createNormal flow.appendBlock(createNormal
(ma, instr, new ConvertOperator (ma, instr, new ConvertOperator
(types[UNARY_TYPES][0], types[I2BCS_TYPES][opcode-opc_i2b])); (types[UNARY_TYPES][0], types[I2BCS_TYPES][opcode-opc_i2b])));
break;
case opc_lcmp: case opc_lcmp:
case opc_fcmpl: case opc_fcmpg: case opc_fcmpl: case opc_fcmpg:
case opc_dcmpl: case opc_dcmpg: case opc_dcmpl: case opc_dcmpg:
return createNormal flow.appendBlock(createNormal
(ma, instr, new CompareToIntOperator (ma, instr, new CompareToIntOperator
(types[BIN_TYPES][(opcode-(opc_lcmp-3))/2], (types[BIN_TYPES][(opcode-(opc_lcmp-3))/2],
(opcode == opc_fcmpg || opcode == opc_dcmpg))); (opcode == opc_fcmpg || opcode == opc_dcmpg))));
break;
case opc_ifeq: case opc_ifne: case opc_ifeq: case opc_ifne:
return createIfGoto flow.appendBlock(createIfGoto
(ma, instr, (ma, instr,
new CompareUnaryOperator new CompareUnaryOperator
(Type.tBoolInt, opcode - (opc_ifeq-Operator.COMPARE_OP))); (Type.tBoolInt, opcode - (opc_ifeq-Operator.COMPARE_OP))));
break;
case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle:
return createIfGoto flow.appendBlock(createIfGoto
(ma, instr, (ma, instr,
new CompareUnaryOperator new CompareUnaryOperator
(Type.tInt, opcode - (opc_ifeq-Operator.COMPARE_OP))); (Type.tInt, opcode - (opc_ifeq-Operator.COMPARE_OP))));
break;
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpeq: case opc_if_icmpne:
return createIfGoto flow.appendBlock
(ma, instr, (createIfGoto
new CompareBinaryOperator (ma, instr,
(tBoolIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP))); new CompareBinaryOperator
(tBoolIntHint,
opcode - (opc_if_icmpeq-Operator.COMPARE_OP))));
break;
case opc_if_icmplt: case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmpgt: case opc_if_icmple:
return createIfGoto flow.appendBlock
(ma, instr, (createIfGoto
new CompareBinaryOperator (ma, instr,
(tIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP))); new CompareBinaryOperator
(tIntHint, opcode - (opc_if_icmpeq-Operator.COMPARE_OP))));
break;
case opc_if_acmpeq: case opc_if_acmpne: case opc_if_acmpeq: case opc_if_acmpne:
return createIfGoto flow.appendBlock
(ma, instr, (createIfGoto
new CompareBinaryOperator (ma, instr,
(Type.tUObject, new CompareBinaryOperator
opcode - (opc_if_acmpeq-Operator.COMPARE_OP))); (Type.tUObject,
case opc_goto: opcode - (opc_if_acmpeq-Operator.COMPARE_OP))));
return createGoto(ma, instr); break;
case opc_jsr: case opc_jsr:
return createJsr(ma, instr); flow.appendBlock(createJsr(ma, instr));
case opc_ret: break;
return createRet case opc_ret: {
(ma, instr, LocalInfo local = ma.getLocalInfo(instr.getLocalInfo());
ma.getLocalInfo(instr.getAddr(), instr.getLocalSlot())); flow.appendReadBlock(createRet(ma, instr, local), local);
break;
}
case opc_lookupswitch: { case opc_lookupswitch: {
int[] cases = instr.getValues(); int[] cases = instr.getValues();
FlowBlock[] dests = new FlowBlock[instr.getSuccs().length]; flow.appendBlock(createSwitch(ma, instr, cases));
for (int i=0; i < dests.length; i++) break;
dests[i] = (FlowBlock) instr.getSuccs()[i].getTmpInfo();
dests[cases.length] = (FlowBlock)
instr.getSuccs()[cases.length].getTmpInfo();
return createSwitch(ma, instr, cases, dests);
} }
case opc_ireturn: case opc_lreturn: case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn: { case opc_freturn: case opc_dreturn: case opc_areturn: {
Type retType = Type.tSubType(ma.getReturnType()); Type retType = Type.tSubType(ma.getReturnType());
return createBlock flow.appendBlock
(ma, instr, new ReturnBlock(new NopOperator(retType))); (createBlock
(ma, instr, new ReturnBlock(new NopOperator(retType))));
break;
} }
case opc_return: case opc_return:
return createBlock flow.appendBlock(createBlock
(ma, instr, new EmptyBlock(new Jump(FlowBlock.END_OF_METHOD))); (ma, instr, new ReturnBlock()));
break;
case opc_getstatic: case opc_getstatic:
case opc_getfield: { case opc_getfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
return createNormal flow.appendBlock(createNormal
(ma, instr, new GetFieldOperator (ma, instr, new GetFieldOperator
(ma, opcode == opc_getstatic, ref)); (ma, opcode == opc_getstatic, ref)));
break;
} }
case opc_putstatic: case opc_putstatic:
case opc_putfield: { case opc_putfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
return createNormal flow.appendBlock
(ma, instr, new StoreInstruction (createNormal
(new PutFieldOperator(ma, opcode == opc_putstatic, ref))); (ma, instr, new StoreInstruction
(new PutFieldOperator(ma, opcode == opc_putstatic, ref))));
break;
} }
case opc_invokevirtual: case opc_invokevirtual:
case opc_invokespecial: case opc_invokespecial:
@ -338,51 +369,64 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
: InvokeOperator.VIRTUAL); : InvokeOperator.VIRTUAL);
StructuredBlock block = createNormal StructuredBlock block = createNormal
(ma, instr, new InvokeOperator(ma, flag, ref)); (ma, instr, new InvokeOperator(ma, flag, ref));
return block; flow.appendBlock(block);
break;
} }
case opc_new: { case opc_new: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
return createNormal(ma, instr, new NewOperator(type)); flow.appendBlock(createNormal(ma, instr, new NewOperator(type)));
break;
} }
case opc_arraylength: case opc_arraylength:
return createNormal flow.appendBlock(createNormal
(ma, instr, new ArrayLengthOperator()); (ma, instr, new ArrayLengthOperator()));
break;
case opc_athrow: case opc_athrow:
return createBlock flow.appendBlock(createBlock
(ma, instr, (ma, instr,
new ThrowBlock(new NopOperator(Type.tUObject))); new ThrowBlock(new NopOperator(Type.tUObject))));
break;
case opc_checkcast: { case opc_checkcast: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
return createNormal flow.appendBlock(createNormal
(ma, instr, new CheckCastOperator(type)); (ma, instr, new CheckCastOperator(type)));
break;
} }
case opc_instanceof: { case opc_instanceof: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
return createNormal flow.appendBlock(createNormal
(ma, instr, new InstanceOfOperator(type)); (ma, instr, new InstanceOfOperator(type)));
break;
} }
case opc_monitorenter: case opc_monitorenter:
return createNormal(ma, instr, flow.appendBlock(createNormal(ma, instr,
new MonitorEnterOperator()); new MonitorEnterOperator()));
break;
case opc_monitorexit: case opc_monitorexit:
return createNormal(ma, instr, flow.appendBlock(createNormal(ma, instr,
new MonitorExitOperator()); new MonitorExitOperator()));
break;
case opc_multianewarray: { case opc_multianewarray: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(instr.getClazzType());
ma.useType(type); ma.useType(type);
int dimension = instr.getDimensions(); int dimension = instr.getDimensions();
return createNormal(ma, instr, flow.appendBlock(createNormal
new NewArrayOperator(type, dimension)); (ma, instr,
} new NewArrayOperator(type, dimension)));
break;
}
case opc_ifnull: case opc_ifnonnull: case opc_ifnull: case opc_ifnonnull:
return createIfGoto flow.appendBlock(createIfGoto
(ma, instr, new CompareUnaryOperator (ma, instr, new CompareUnaryOperator
(Type.tUObject, opcode - (opc_ifnull-Operator.COMPARE_OP))); (Type.tUObject,
opcode - (opc_ifnull-Operator.COMPARE_OP))));
break;
default: default:
throw new jode.AssertError("Invalid opcode "+opcode); throw new jode.AssertError("Invalid opcode "+opcode);
} }
} }
} }

@ -19,7 +19,7 @@
package jode.decompiler; package jode.decompiler;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo; import java.io.IOException;
public class Options { public class Options {
public static final int TAB_SIZE_MASK = 0x0f; public static final int TAB_SIZE_MASK = 0x0f;
@ -53,14 +53,14 @@ public class Options {
} }
public static boolean skipClass(ClassInfo clazz) { public static boolean skipClass(ClassInfo clazz) {
InnerClassInfo[] outers = clazz.getOuterClasses(); if (!doInner() && !doAnonymous())
if (outers != null) { return false;
if (outers[0].outer == null) { try {
return doAnonymous(); clazz.load(ClassInfo.OUTERCLASS);
} else { } catch (IOException ex) {
return doInner(); return false;
}
} }
return false; return (doInner() && clazz.getOuterClass() != null
|| doAnonymous() && clazz.isMethodScoped());
} }
} }

@ -18,13 +18,6 @@
*/ */
package jode.decompiler; 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 * This interface is used by jode to tell about its progress. You

@ -37,19 +37,24 @@ public interface Scope {
public final int NOSUPERMETHODNAME = 12; public final int NOSUPERMETHODNAME = 12;
public final int NOSUPERFIELDNAME = 13; public final int NOSUPERFIELDNAME = 13;
/**
* Tells that we want to allow a classanalyzer as scope.
*/
public final int CLASSSCOPE = 1; public final int CLASSSCOPE = 1;
public final int METHODSCOPE = 2;
/** /**
* Simplifies the given name. * Tells that we want to allow a methodanalyzer as scope.
* @param name the name to simplify.
* @param usageType the context of this name.
* @return null if the name hasn't a simplification in current
* scope, the simplified name otherwise.
*/ */
public final int METHODSCOPE = 2;
/** /**
* Tells if this is the scope of name * Tells if this is the scope of the given object, which is of
* scopeType.
* @param object the object for which the scope
* @param usageType either CLASSCOPE or METHODSCOPE
* @return true if the given object is in this scope.
*/ */
public boolean isScopeOf(Object object, int scopeType); public boolean isScopeOf(Object object, int scopeType);
public boolean conflicts(String name, int usageType); public boolean conflicts(String name, int usageType);
} }

@ -25,7 +25,6 @@ import java.util.Enumeration;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
import jode.type.*; import jode.type.*;
public class TabbedPrintWriter { public class TabbedPrintWriter {
@ -632,91 +631,43 @@ public class TabbedPrintWriter {
return null; return null;
} }
public String getInnerClassString(ClassInfo info, int scopeType) { public String getClassString(ClassInfo clazz, int scopeType) {
InnerClassInfo[] outers = info.getOuterClasses(); if ((Options.options & Options.OPTION_INNER) != 0
if (outers == null) && clazz.getOuterClass() != null) {
return null;
for (int i=0; i< outers.length; i++) { String className = clazz.getClassName();
if (outers[i].name == null || outers[i].outer == null) Scope scope = getScope(clazz.getOuterClass(), Scope.CLASSSCOPE);
return null;
Scope scope = getScope(ClassInfo.forName(outers[i].outer),
Scope.CLASSSCOPE);
if (scope != null && if (scope != null &&
!conflicts(outers[i].name, scope, scopeType)) { !conflicts(className, scope, scopeType))
StringBuffer sb = new StringBuffer(outers[i].name); return className;
for (int j = i; j-- > 0;) {
sb.append('.').append(outers[j].name); return getClassString(clazz.getOuterClass(), scopeType)
} + "." + className;
return sb.toString();
}
} }
String name = getClassString
(ClassInfo.forName(outers[outers.length-1].outer), scopeType); if ((Options.options & Options.OPTION_ANON) != 0
StringBuffer sb = new StringBuffer(name); && clazz.isMethodScoped()) {
for (int j = outers.length; j-- > 0;)
sb.append('.').append(outers[j].name); String className = clazz.getClassName();
return sb.toString(); if (className == null)
} return "ANONYMOUS CLASS "+clazz.getName();
public String getAnonymousClassString(ClassInfo info, int scopeType) { Scope scope = getScope(clazz, Scope.METHODSCOPE);
InnerClassInfo[] outers = info.getOuterClasses();
if (outers == null)
return null;
for (int i=0; i< outers.length; i++) {
if (outers[i].name == null)
return "ANONYMOUS CLASS "+info.getName();
Scope scope = getScope(info, Scope.METHODSCOPE);
if (scope != null && if (scope != null &&
!conflicts(outers[i].name, scope, scopeType)) { !conflicts(className, scope, scopeType))
StringBuffer sb = new StringBuffer(outers[i].name); return className;
for (int j = i; j-- > 0;) {
sb.append('.').append(outers[j].name); if (scope != null)
} return "NAME CONFLICT " + className;
return sb.toString(); else
} else if (outers[i].outer == null) { return "UNREACHABLE " + className;
StringBuffer sb;
if (scope != null)
sb = new StringBuffer("NAME CONFLICT ");
else
sb = new StringBuffer("UNREACHABLE ");
sb.append(outers[i].name);
for (int j = i; j-- > 0;) {
sb.append('.').append(outers[j].name);
}
return sb.toString();
}
}
String name = getClassString
(ClassInfo.forName(outers[outers.length-1].outer), scopeType);
StringBuffer sb = new StringBuffer(name);
for (int j = outers.length; j-- > 0;)
sb.append('.').append(outers[j].name);
return sb.toString();
}
public String getClassString(ClassInfo clazz, int scopeType) {
String name = clazz.getName();
if (name.indexOf('$') >= 0) {
if ((Options.options & Options.OPTION_INNER) != 0) {
String innerClassName
= getInnerClassString(clazz, scopeType);
if (innerClassName != null)
return innerClassName;
}
if ((Options.options
& Options.OPTION_ANON) != 0) {
String innerClassName
= getAnonymousClassString(clazz, scopeType);
if (innerClassName != null)
return innerClassName;
}
} }
if (imports != null) { if (imports != null) {
String importedName = imports.getClassString(clazz); String importedName = imports.getClassString(clazz);
if (!conflicts(importedName, null, scopeType)) if (!conflicts(importedName, null, scopeType))
return importedName; return importedName;
} }
String name = clazz.getName();
if (conflicts(name, null, Scope.AMBIGUOUSNAME)) if (conflicts(name, null, Scope.AMBIGUOUSNAME))
return "PKGNAMECONFLICT "+ name; return "PKGNAMECONFLICT "+ name;
return name; return name;

@ -22,7 +22,9 @@ import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
/** /**
* This is a pseudo operator, which represents the check against null * This is a pseudo operator, which represents the check against null

@ -20,6 +20,7 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.type.IntegerType; import jode.type.IntegerType;
import jode.util.StringQuoter;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class ConstOperator extends NoArgOperator { public class ConstOperator extends NoArgOperator {
@ -113,46 +114,6 @@ public class ConstOperator extends NoArgOperator {
isInitializer = true; isInitializer = true;
} }
private static String quoted(String str) {
StringBuffer result = new StringBuffer("\"");
for (int i=0; i< str.length(); i++) {
char c;
switch (c = str.charAt(i)) {
case '\0':
result.append("\\0");
break;
case '\t':
result.append("\\t");
break;
case '\n':
result.append("\\n");
break;
case '\r':
result.append("\\r");
break;
case '\\':
result.append("\\\\");
break;
case '\"':
result.append("\\\"");
break;
default:
if (c < 32) {
String oct = Integer.toOctalString(c);
result.append("\\000".substring(0, 4-oct.length()))
.append(oct);
} else if (c >= 32 && c < 127)
result.append(str.charAt(i));
else {
String hex = Integer.toHexString(c);
result.append("\\u0000".substring(0, 6-hex.length()))
.append(hex);
}
}
}
return result.append("\"").toString();
}
public String toString() { public String toString() {
String strVal = String.valueOf(value); String strVal = String.valueOf(value);
if (type.isOfType(Type.tBoolean)) { if (type.isOfType(Type.tBoolean)) {
@ -167,34 +128,9 @@ public class ConstOperator extends NoArgOperator {
} }
if (type.getHint().equals(Type.tChar)) { if (type.getHint().equals(Type.tChar)) {
char c = (char) ((Integer) value).intValue(); char c = (char) ((Integer) value).intValue();
switch (c) { return StringQuoter.quote(c);
case '\0':
return "\'\\0\'";
case '\t':
return "\'\\t\'";
case '\n':
return "\'\\n\'";
case '\r':
return "\'\\r\'";
case '\\':
return "\'\\\\\'";
case '\"':
return "\'\\\"\'";
case '\'':
return "\'\\\'\'";
}
if (c < 32) {
String oct = Integer.toOctalString(c);
return "\'\\000".substring(0, 5-oct.length())+oct+"\'";
}
if (c >= 32 && c < 127)
return "\'"+c+"\'";
else {
String hex = Integer.toHexString(c);
return "\'\\u0000".substring(0, 7-hex.length())+hex+"\'";
}
} else if (type.equals(Type.tString)) { } else if (type.equals(Type.tString)) {
return quoted(strVal); return StringQuoter.quote(strVal);
} else if (parent != null) { } else if (parent != null) {
int opindex = parent.getOperatorIndex(); int opindex = parent.getOperatorIndex();
if (opindex >= OPASSIGN_OP + ADD_OP if (opindex >= OPASSIGN_OP + ADD_OP

@ -22,8 +22,10 @@ import jode.type.Type;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collection;
import java.util.Set;
///#enddef
public abstract class Expression { public abstract class Expression {
protected Type type; protected Type type;

@ -18,13 +18,13 @@
*/ */
package jode.expr; package jode.expr;
import jode.GlobalOptions;
import jode.type.Type; import jode.type.Type;
import jode.type.NullType; import jode.type.NullType;
import jode.type.ClassInterfacesType; import jode.type.ClassInterfacesType;
import jode.bytecode.FieldInfo; import jode.bytecode.FieldInfo;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.bytecode.InnerClassInfo;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
import jode.decompiler.ClassAnalyzer; import jode.decompiler.ClassAnalyzer;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
@ -33,7 +33,10 @@ import jode.decompiler.Options;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import jode.decompiler.Scope; import jode.decompiler.Scope;
import @COLLECTIONS@.Collection; import java.io.IOException;
///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
/** /**
* This class contains everything shared between PutFieldOperator and * This class contains everything shared between PutFieldOperator and
@ -44,6 +47,8 @@ public abstract class FieldOperator extends Operator {
boolean staticFlag; boolean staticFlag;
Reference ref; Reference ref;
Type classType; Type classType;
ClassInfo classInfo;
String callerPackage;
public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag, public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag,
Reference ref) { Reference ref) {
@ -55,6 +60,21 @@ public abstract class FieldOperator extends Operator {
if (staticFlag) if (staticFlag)
methodAnalyzer.useType(classType); methodAnalyzer.useType(classType);
initOperands(staticFlag ? 0 : 1); initOperands(staticFlag ? 0 : 1);
callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName();
int dot = callerPackage.lastIndexOf('.');
callerPackage = callerPackage.substring(0, dot);
if (classType instanceof ClassInterfacesType) {
classInfo = ((ClassInterfacesType) classType).getClassInfo();
if ((Options.options & Options.OPTION_ANON) != 0
|| (Options.options & Options.OPTION_INNER) != 0) {
try {
classInfo.load(ClassInfo.OUTERCLASS);
} catch (IOException ex) {
classInfo.guess(ClassInfo.OUTERCLASS);
}
}
}
} }
public int getPriority() { public int getPriority() {
@ -75,9 +95,7 @@ public abstract class FieldOperator extends Operator {
} }
public ClassInfo getClassInfo() { public ClassInfo getClassInfo() {
if (classType instanceof ClassInterfacesType) return classInfo;
return ((ClassInterfacesType) classType).getClassInfo();
return null;
} }
/** /**
@ -87,7 +105,7 @@ public abstract class FieldOperator extends Operator {
* @return see above. * @return see above.
*/ */
public FieldAnalyzer getField() { public FieldAnalyzer getField() {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = classInfo;
if (clazz != null) { if (clazz != null) {
ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
while (true) { while (true) {
@ -120,6 +138,21 @@ public abstract class FieldOperator extends Operator {
return Type.tType(ref.getType()); return Type.tType(ref.getType());
} }
private FieldInfo[] loadFields(ClassInfo clazz) {
int howMuch = (clazz.getName().startsWith(callerPackage)
&& (clazz.getName().lastIndexOf('.')
< callerPackage.length()))
? ClassInfo.DECLARATIONS : ClassInfo.PUBLICDECLARATIONS;
try {
clazz.load(howMuch);
} catch (IOException ex) {
GlobalOptions.err.println("Warning: Can't find fields of "
+clazz+" to detect hiding conflicts");
clazz.guess(howMuch);
}
return clazz.getFields();
}
public boolean needsCast(Type type) { public boolean needsCast(Type type) {
if (type instanceof NullType) if (type instanceof NullType)
return true; return true;
@ -140,25 +173,16 @@ public abstract class FieldOperator extends Operator {
return false; return false;
} }
public InnerClassInfo getOuterClassInfo(ClassInfo ci) {
if (ci != null) {
InnerClassInfo[] outers = ci.getOuterClasses();
if (outers != null)
return outers[0];
}
return null;
}
/** /**
* We add the named method scoped classes to the declarables. * We add the named method scoped classes to the declarables.
*/ */
public void fillDeclarables(Collection used) { public void fillDeclarables(Collection used) {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = getClassInfo();
InnerClassInfo outer = getOuterClassInfo(clazz);
ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz); ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
&& outer != null && outer.outer == null && outer.name != null && clazz != null
&& clazz.isMethodScoped() && clazz.getClassName() != null
&& clazzAna != null && clazzAna != null
&& clazzAna.getParent() == methodAnalyzer) { && clazzAna.getParent() == methodAnalyzer) {

@ -34,12 +34,16 @@ import jode.type.*;
import jode.util.SimpleMap; import jode.util.SimpleMap;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable; import java.io.IOException;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Collection; import java.util.Collection;
import @COLLECTIONS@.Map; import java.util.Collections;
import @COLLECTIONS@.Iterator; import java.util.Map;
import @COLLECTIONS@.Set; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
///#enddef
public final class InvokeOperator extends Operator public final class InvokeOperator extends Operator
implements MatchableOperator { implements MatchableOperator {
@ -61,9 +65,11 @@ public final class InvokeOperator extends Operator
int skippedArgs; int skippedArgs;
Type classType; Type classType;
Type[] hints; Type[] hints;
ClassInfo classInfo;
String callerPackage;
/** /**
* This hashtable contains hints for every library method. Some * This hash map contains hints for every library method. Some
* library method take or return an int, but it should be a char * library method take or return an int, but it should be a char
* instead. We will remember that here to give them the right * instead. We will remember that here to give them the right
* hint. * hint.
@ -75,10 +81,10 @@ public final class InvokeOperator extends Operator
* remaining entries are the hint types of the parameters. All * remaining entries are the hint types of the parameters. All
* hint types may be null, if that parameter shouldn't be hinted. * hint types may be null, if that parameter shouldn't be hinted.
*/ */
private final static Hashtable hintTypes = new Hashtable(); private final static HashMap hintTypes = new HashMap();
static { static {
/* Fill the hint type hashtable. For example, the first /* Fill the hint type hash map. For example, the first
* parameter of String.indexOf should be hinted as char, even * parameter of String.indexOf should be hinted as char, even
* though the formal parameter is an int. * though the formal parameter is an int.
* First hint is hint of return value (even if void) * First hint is hint of return value (even if void)
@ -151,6 +157,21 @@ public final class InvokeOperator extends Operator
methodAnalyzer.useType(classType); methodAnalyzer.useType(classType);
skippedArgs = (methodFlag == STATIC ? 0 : 1); skippedArgs = (methodFlag == STATIC ? 0 : 1);
initOperands(skippedArgs + methodType.getParameterTypes().length); initOperands(skippedArgs + methodType.getParameterTypes().length);
callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName();
int dot = callerPackage.lastIndexOf('.');
callerPackage = callerPackage.substring(0, dot);
if (classType instanceof ClassInterfacesType) {
classInfo = ((ClassInterfacesType) classType).getClassInfo();
if ((Options.options & Options.OPTION_ANON) != 0
|| (Options.options & Options.OPTION_INNER) != 0) {
try {
classInfo.load(ClassInfo.OUTERCLASS);
} catch (IOException ex) {
classInfo.guess(ClassInfo.OUTERCLASS);
}
}
}
checkAnonymousClasses(); checkAnonymousClasses();
} }
@ -178,10 +199,10 @@ public final class InvokeOperator extends Operator
if (methodFlag != CONSTRUCTOR if (methodFlag != CONSTRUCTOR
|| (Options.options & Options.OPTION_ANON) == 0) || (Options.options & Options.OPTION_ANON) == 0)
return; return;
InnerClassInfo outer = getOuterClassInfo(getClassInfo()); if (classInfo != null
if (outer != null && (outer.outer == null || outer.name == null)) { && classInfo.isMethodScoped()
&& classInfo.getClassName() == null)
methodAnalyzer.addAnonymousConstructor(this); methodAnalyzer.addAnonymousConstructor(this);
}
} }
public void updateSubTypes() { public void updateSubTypes() {
@ -206,9 +227,9 @@ public final class InvokeOperator extends Operator
public void makeNonVoid() { public void makeNonVoid() {
if (type != Type.tVoid) if (type != Type.tVoid)
throw new jode.AssertError("already non void"); throw new jode.AssertError("already non void");
ClassInfo clazz = getClassInfo(); ClassInfo clazz = classInfo;
InnerClassInfo outer = getOuterClassInfo(clazz); if (clazz != null
if (outer != null && outer.name == null) { && clazz.isMethodScoped() && clazz.getClassName() == null) {
/* This is an anonymous class */ /* This is an anonymous class */
if (clazz.getInterfaces().length > 0) if (clazz.getInterfaces().length > 0)
type = Type.tClass(clazz.getInterfaces()[0]); type = Type.tClass(clazz.getInterfaces()[0]);
@ -223,66 +244,46 @@ public final class InvokeOperator extends Operator
} }
public ClassInfo getClassInfo() { public ClassInfo getClassInfo() {
if (classType instanceof ClassInterfacesType) return classInfo;
return ((ClassInterfacesType) classType).getClassInfo();
return null;
} }
/** /**
* Checks, whether this is a call of a method from this class. * Checks, whether this is a call of a method from this class.
*/ */
public boolean isThis() { public boolean isThis() {
return getClassInfo() == methodAnalyzer.getClazz(); return classInfo == methodAnalyzer.getClazz();
}
public InnerClassInfo getOuterClassInfo(ClassInfo ci) {
if (ci != null) {
InnerClassInfo[] outers = ci.getOuterClasses();
if (outers != null)
return outers[0];
}
return null;
} }
/** /**
* Tries to locate the class analyzer for the callee class. This * Tries to locate the class analyzer for the callee class. This
* is mainly useful for inner and anonymous classes. * is mainly useful for inner and anonymous classes.
* *
* @param callee the callee class.
* @return The class analyzer, if the callee class is declared * @return The class analyzer, if the callee class is declared
* inside the same base class as the caller class, null otherwise. * inside the same base class as the caller class, null otherwise.
*/ */
public ClassAnalyzer getClassAnalyzer() { private ClassAnalyzer getClassAnalyzer(ClassInfo callee) {
if ((Options.options & if ((Options.options &
(Options.OPTION_ANON | Options.OPTION_INNER)) == 0) (Options.OPTION_ANON | Options.OPTION_INNER)) == 0)
return null; return null;
ClassInfo callee = getClassInfo();
if (callee == null)
return null;
int nested = 0;
InnerClassInfo[] outers = callee.getOuterClasses();
if ((Options.options & Options.OPTION_INNER) != 0 if ((Options.options & Options.OPTION_INNER) != 0
&& outers != null) { && callee.getOuterClass() != null) {
/* If the callee class is an inner class we take its /* If the callee class is an inner class we get the
* (outermost) parent instead. This will assure that we * analyzer of its parent instead and ask it for the inner
* find the callee class with one inner -> outer pass. * class analyzer.
*/ */
nested = outers.length; ClassAnalyzer outerAna = getClassAnalyzer(callee.getOuterClass());
if (outers[nested - 1].outer == null return outerAna == null ? null
|| outers[nested - 1].name == null) : outerAna.getInnerClassAnalyzer(callee.getClassName());
nested--;
if (nested > 0)
callee = ClassInfo.forName(outers[nested - 1].outer);
} }
/* First check if it is an inner class */ /* First check if our methodAnlyzer knows about it */
ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(callee); ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(callee);
if (ana == null) { if (ana == null) {
/* Now we iterate the caller analyzer queue to find the class /* Now we iterate through the parent clazz analyzers until
* analyzer for callee * we find the class analyzer for callee.
*/ */
ana = methodAnalyzer.getClassAnalyzer(); ana = methodAnalyzer.getClassAnalyzer();
while (callee != ana.getClazz()) { while (callee != ana.getClazz()) {
@ -301,17 +302,20 @@ public final class InvokeOperator extends Operator
("Unknown parent: "+ana+": "+ana.getParent()); ("Unknown parent: "+ana+": "+ana.getParent());
} }
} }
/* Now get the ClassAnalyzer of the real callee */
while (nested > 0) {
nested--;
ana = ana.getInnerClassAnalyzer(outers[nested].name);
if (ana == null)
return null;
}
return ana; return ana;
} }
/**
* Tries to locate the class analyzer for the callee class. This
* is mainly useful for inner and anonymous classes.
*
* @return The class analyzer, if the callee class is declared
* inside the same base class as the caller class, null otherwise.
*/
public ClassAnalyzer getClassAnalyzer() {
return getClassAnalyzer(classInfo);
}
/** /**
* Checks, whether this is a call of a method from this class or an * Checks, whether this is a call of a method from this class or an
* outer instance. * outer instance.
@ -349,7 +353,7 @@ public final class InvokeOperator extends Operator
* inside the same base class as the caller class, null otherwise. * inside the same base class as the caller class, null otherwise.
*/ */
public MethodAnalyzer getMethodAnalyzer() { public MethodAnalyzer getMethodAnalyzer() {
ClassAnalyzer ana = getClassAnalyzer(); ClassAnalyzer ana = getClassAnalyzer(classInfo);
if (ana == null) if (ana == null)
return null; return null;
return ana.getMethod(methodName, methodType); return ana.getMethod(methodName, methodType);
@ -357,12 +361,17 @@ public final class InvokeOperator extends Operator
/** /**
* Checks, whether this is a call of a method from the super class. * Checks, whether this is a call of a method from the super class.
* @XXX check, if its the first super class that implements the method.
*/ */
public boolean isSuperOrThis() { public boolean isSuperOrThis() {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = getClassInfo();
if (clazz != null) { if (clazz != null) {
return clazz.superClassOf(methodAnalyzer.getClazz()); try {
return clazz.superClassOf(methodAnalyzer.getClazz());
} catch (IOException ex) {
/* Assume it is not a super class. This will print
* a warning.
*/
}
} }
return false; return false;
} }
@ -371,16 +380,16 @@ public final class InvokeOperator extends Operator
if ((Options.options & Options.OPTION_ANON) == 0) if ((Options.options & Options.OPTION_ANON) == 0)
return super.isConstant(); return super.isConstant();
ClassInfo clazz = getClassInfo(); ClassInfo clazz = classInfo;
InnerClassInfo outer = getOuterClassInfo(clazz); if (clazz != null
ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz); && clazz.isMethodScoped() && clazz.getClassName() != null) {
if (clazzAna != null ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
&& outer != null && outer.outer == null && outer.name != null if (clazzAna != null && clazzAna.getParent() == methodAnalyzer)
&& clazzAna.getParent() == methodAnalyzer) { /* This is a named class of this method, it needs
/* This is a named method scope class, it needs * declaration. And therefore can't be moved into a
* declaration. And therefore can't be moved into * field initializer.
* a field initializer. */ */
return false; return false;
} }
return super.isConstant(); return super.isConstant();
} }
@ -409,22 +418,23 @@ public final class InvokeOperator extends Operator
class Environment extends SimpleRuntimeEnvironment { class Environment extends SimpleRuntimeEnvironment {
Interpreter interpreter; Interpreter interpreter;
ClassInfo classInfo;
String classSig; String classSig;
public Environment(String interpretedClassSig) { public Environment(ClassInfo classInfo) {
classSig = interpretedClassSig.intern(); this.classInfo = classInfo;
this.classSig = "L" + classInfo.getName().replace('.','/') + ";";
} }
public Object invokeMethod(Reference ref, boolean isVirtual, public Object invokeMethod(Reference ref, boolean isVirtual,
Object cls, Object[] params) Object cls, Object[] params)
throws InterpreterException, InvocationTargetException { throws InterpreterException, InvocationTargetException {
if (cls == null && ref.getClazz().equals(classSig)) { if (cls == null && ref.getClazz().equals(classSig)) {
BytecodeInfo info = BasicBlocks bb = classInfo
ClassInfo.forName(ref.getClazz())
.findMethod(ref.getName(), ref.getType()) .findMethod(ref.getName(), ref.getType())
.getBytecode(); .getBasicBlocks();
if (info != null) if (bb != null)
return interpreter.interpretMethod(info, null, params); return interpreter.interpretMethod(bb, null, params);
throw new InterpreterException throw new InterpreterException
("Can't interpret static native method: "+ref); ("Can't interpret static native method: "+ref);
} else } else
@ -437,15 +447,14 @@ public final class InvokeOperator extends Operator
MethodAnalyzer ma = clazz.getMethod(methodName, methodType); MethodAnalyzer ma = clazz.getMethod(methodName, methodType);
if (ma == null) if (ma == null)
return null; return null;
Environment env = new Environment("L"+methodAnalyzer.getClazz() Environment env = new Environment(methodAnalyzer.getClazz());
.getName().replace('.','/')+";");
Interpreter interpreter = new Interpreter(env); Interpreter interpreter = new Interpreter(env);
env.interpreter = interpreter; env.interpreter = interpreter;
String result; String result;
try { try {
result = (String) interpreter.interpretMethod result = (String) interpreter.interpretMethod
(ma.getBytecodeInfo(), null, new Object[] { op.getValue() }); (ma.getBasicBlocks(), null, new Object[] { op.getValue() });
} catch (InterpreterException ex) { } catch (InterpreterException ex) {
if ((GlobalOptions.debuggingFlags & if ((GlobalOptions.debuggingFlags &
GlobalOptions.DEBUG_INTERPRT) != 0) { GlobalOptions.DEBUG_INTERPRT) != 0) {
@ -629,6 +638,21 @@ public final class InvokeOperator extends Operator
return null; return null;
} }
private MethodInfo[] loadMethods(ClassInfo clazz) {
int howMuch = (clazz.getName().startsWith(callerPackage)
&& (clazz.getName().lastIndexOf('.')
< callerPackage.length()))
? ClassInfo.DECLARATIONS : ClassInfo.PUBLICDECLARATIONS;
try {
clazz.load(howMuch);
} catch (IOException ex) {
GlobalOptions.err.println("Warning: Can't find methods of "
+clazz+" to detect overload conflicts");
clazz.guess(howMuch);
}
return clazz.getMethods();
}
public boolean needsCast(int param, Type[] paramTypes) { public boolean needsCast(int param, Type[] paramTypes) {
Type realClassType; Type realClassType;
if (methodFlag == STATIC) if (methodFlag == STATIC)
@ -654,7 +678,7 @@ public final class InvokeOperator extends Operator
/* Now check if there is a conflicting method in this class or /* Now check if there is a conflicting method in this class or
* a superclass. */ * a superclass. */
while (clazz != null) { while (clazz != null) {
MethodInfo[] methods = clazz.getMethods(); MethodInfo[] methods = loadMethods(clazz);
next_method: next_method:
for (int i=0; i< methods.length; i++) { for (int i=0; i< methods.length; i++) {
if (!methods[i].getName().equals(methodName)) if (!methods[i].getName().equals(methodName))
@ -703,21 +727,21 @@ public final class InvokeOperator extends Operator
* only fillDeclarables on the parameters we will print. * only fillDeclarables on the parameters we will print.
*/ */
public void fillDeclarables(Collection used) { public void fillDeclarables(Collection used) {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = classInfo;
InnerClassInfo outer = getOuterClassInfo(clazz);
ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz); ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
&& outer != null && outer.outer == null && outer.name != null && clazz != null
&& clazzAna != null && clazz.isMethodScoped() && clazz.getClassName() != null) {
&& clazzAna.getParent() == methodAnalyzer) {
if (clazzAna != null && clazzAna.getParent() == methodAnalyzer) {
/* This is a named method scope class, declare it. /* This is a named method scope class, declare it.
* But first declare all method scoped classes, * But first declare all method scoped classes,
* that are used inside; order does matter. * that are used inside; order does matter.
*/ */
clazzAna.fillDeclarables(used); clazzAna.fillDeclarables(used);
used.add(clazzAna); used.add(clazzAna);
}
} }
if (!isConstructor() || isStatic()) { if (!isConstructor() || isStatic()) {
@ -729,8 +753,7 @@ public final class InvokeOperator extends Operator
boolean jikesAnonymousInner = false; boolean jikesAnonymousInner = false;
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
&& clazzAna != null && clazzAna != null && clazz.isMethodScoped()) {
&& outer != null && (outer.outer == null || outer.name == null)) {
OuterValues ov = clazzAna.getOuterValues(); OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount(); arg += ov.getCount();
@ -745,26 +768,23 @@ public final class InvokeOperator extends Operator
expr.fillDeclarables(used); expr.fillDeclarables(used);
} }
if (outer.name == null) { if (clazz.getClassName() == null) {
/* This is an anonymous class */ /* This is an anonymous class */
ClassInfo superClazz = clazz.getSuperclass(); ClassInfo superClazz = clazz.getSuperclass();
ClassInfo[] interfaces = clazz.getInterfaces(); ClassInfo[] interfaces = clazz.getInterfaces();
if (interfaces.length == 1 if (interfaces.length == 1
&& (superClazz == null && (superClazz == null
|| superClazz == ClassInfo.javaLangObject)) { || superClazz.getName() == "java.lang.Object")) {
clazz = interfaces[0]; clazz = interfaces[0];
} else { } else {
clazz = (superClazz != null clazz = superClazz;
? superClazz : ClassInfo.javaLangObject);
} }
outer = getOuterClassInfo(clazz);
} }
} }
if ((Options.options & Options.OPTION_INNER) != 0 if ((Options.options & Options.OPTION_INNER) != 0
&& outer != null && outer.outer != null && outer.name != null && clazz.getOuterClass() != null
&& !Modifier.isStatic(outer.modifiers)) { && !Modifier.isStatic(clazz.getModifiers())) {
Expression outerExpr = jikesAnonymousInner Expression outerExpr = jikesAnonymousInner
? subExpressions[--length] ? subExpressions[--length]
@ -788,14 +808,17 @@ public final class InvokeOperator extends Operator
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& (Options.options & Options.OPTION_ANON) != 0) { && (Options.options & Options.OPTION_ANON) != 0) {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = classInfo;
InnerClassInfo outer = getOuterClassInfo(clazz); if (clazz != null
ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz); && clazz.isMethodScoped() && clazz.getClassName() == null) {
if (clazzAna != null && outer != null && outer.name == null) { ClassAnalyzer clazzAna
= methodAnalyzer.getClassAnalyzer(clazz);
/* call makeDeclaration on the anonymous class, since /* call makeDeclaration on the anonymous class, since
* _we_ will declare the anonymous class. */ * _we_ will declare the anonymous class.
clazzAna.makeDeclaration(done); */
if (clazzAna != null)
clazzAna.makeDeclaration(done);
} }
} }
} }
@ -804,67 +827,75 @@ public final class InvokeOperator extends Operator
return 5; return 5;
} }
/* Invokes never equals: they may return different values even if
* they have the same parameters.
*/
public void dumpExpression(TabbedPrintWriter writer) public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
/* This is the most complex dumpExpression method you will
* ever find. Most of the complexity is due to handling of
* constructors, especially for inner and method scoped
* classes.
*/
/* All subExpressions from arg to length are arguments. We
* assume a normal virtual method here, otherwise arg and
* length will change later.
*/
int arg = 1; int arg = 1;
int length = subExpressions.length; int length = subExpressions.length;
/* Tells if this is an anonymous constructor */
boolean anonymousNew = false; boolean anonymousNew = false;
ClassInfo clazz = getClassInfo(); /* The ClassInfo for the method we call, null for an array */
ClassInfo clazz = classInfo;
/* The ClassAnalyzer for the method we call (only for inner
* classes), null if we didn't analyze the class. */
ClassAnalyzer clazzAna = null; ClassAnalyzer clazzAna = null;
/* The canonic types of the arguments. Used to see if we need
* casts.
*/
Type[] paramTypes = new Type[subExpressions.length]; Type[] paramTypes = new Type[subExpressions.length];
for (int i=0; i< subExpressions.length; i++) for (int i=0; i< subExpressions.length; i++)
paramTypes[i] = subExpressions[i].getType().getCanonic(); paramTypes[i] = subExpressions[i].getType().getCanonic();
/* Now write the method call. This is the complex part:
* we have to differentiate all kinds of method calls: static,
* virtual, constructor, anonymous constructors, super calls etc.
*/
writer.startOp(writer.NO_PAREN, 0); writer.startOp(writer.NO_PAREN, 0);
switch (methodFlag) { switch (methodFlag) {
case CONSTRUCTOR: { case CONSTRUCTOR: {
boolean qualifiedNew = false; boolean qualifiedNew = false;
boolean jikesAnonymousInner = false; boolean jikesAnonymousInner = false;
/* clazz != null, since an array doesn't have a constructor */
/* Check if this is an anonymous constructor. In this case
* clazz and outer will be changed to point to the
* super class and anonymousNew will be set.
*/
InnerClassInfo outer = getOuterClassInfo(clazz);
if (outer != null && outer.name == null)
anonymousNew = true;
clazzAna = methodAnalyzer.getClassAnalyzer(clazz); clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
if ((~Options.options & if ((~Options.options &
(Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0 (Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0
&& clazzAna != null && clazzAna != null && clazz.isMethodScoped()) {
&& outer != null
&& (outer.outer == null || outer.name == null)) {
/* This is a method scoped class, skip the outerValues */ /* This is a known method scoped class, skip the outerValues */
OuterValues ov = clazzAna.getOuterValues(); OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount(); arg += ov.getCount();
jikesAnonymousInner = ov.isJikesAnonymousInner(); jikesAnonymousInner = ov.isJikesAnonymousInner();
if (outer.name == null) { if (clazz.getClassName() == null) {
/* This is an anonymous class */ /* This is an anonymous class */
anonymousNew = true;
ClassInfo superClazz = clazz.getSuperclass(); ClassInfo superClazz = clazz.getSuperclass();
ClassInfo[] interfaces = clazz.getInterfaces(); ClassInfo[] interfaces = clazz.getInterfaces();
if (interfaces.length == 1 if (interfaces.length == 1
&& (superClazz == null && superClazz.getName() == "java.lang.Object") {
|| superClazz == ClassInfo.javaLangObject)) {
clazz = interfaces[0]; clazz = interfaces[0];
} else { } else {
if (interfaces.length > 0) { if (interfaces.length > 0) {
writer.print("too many supers in ANONYMOUS "); writer.print("too many supers in ANONYMOUS ");
} }
clazz = (superClazz != null clazz = superClazz;
? superClazz : ClassInfo.javaLangObject);
} }
outer = getOuterClassInfo(clazz); if (jikesAnonymousInner && clazz.isMethodScoped()) {
if (jikesAnonymousInner && outer != null
&& outer.outer == null && outer.name != null) {
Expression thisExpr = subExpressions[--length]; Expression thisExpr = subExpressions[--length];
if (thisExpr instanceof CheckNullOperator) { if (thisExpr instanceof CheckNullOperator) {
CheckNullOperator cno CheckNullOperator cno
@ -882,8 +913,8 @@ public final class InvokeOperator extends Operator
/* Check if this is an inner class. It will dump the outer /* Check if this is an inner class. It will dump the outer
* class expression, except if its default. * class expression, except if its default.
*/ */
if (outer != null && outer.outer != null && outer.name != null if (clazz.getOuterClass() != null
&& !Modifier.isStatic(outer.modifiers) && !Modifier.isStatic(clazz.getModifiers())
&& (~Options.options & && (~Options.options &
(Options.OPTION_INNER (Options.OPTION_INNER
| Options.OPTION_CONTRAFO)) == 0) { | Options.OPTION_CONTRAFO)) == 0) {
@ -895,9 +926,9 @@ public final class InvokeOperator extends Operator
CheckNullOperator cno = (CheckNullOperator) outerExpr; CheckNullOperator cno = (CheckNullOperator) outerExpr;
outerExpr = cno.subExpressions[0]; outerExpr = cno.subExpressions[0];
} else if (!(outerExpr instanceof ThisOperator)) { } else if (!(outerExpr instanceof ThisOperator)) {
// Complain about missing checknull, but not if
// that is the known bug in jikes.
if (!jikesAnonymousInner) if (!jikesAnonymousInner)
// Bug in jikes: it doesn't do a check null.
// We don't complain here.
writer.print("MISSING CHECKNULL "); writer.print("MISSING CHECKNULL ");
} }
@ -905,7 +936,8 @@ public final class InvokeOperator extends Operator
Scope scope = writer.getScope Scope scope = writer.getScope
(((ThisOperator) outerExpr).getClassInfo(), (((ThisOperator) outerExpr).getClassInfo(),
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (writer.conflicts(outer.name, scope, Scope.CLASSNAME)) { if (writer.conflicts(clazz.getClassName(),
scope, Scope.CLASSNAME)) {
qualifiedNew = true; qualifiedNew = true;
outerExpr.dumpExpression(writer, 950); outerExpr.dumpExpression(writer, 950);
writer.breakOp(); writer.breakOp();
@ -917,8 +949,7 @@ public final class InvokeOperator extends Operator
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 1); writer.startOp(writer.EXPL_PAREN, 1);
writer.print("("); writer.print("(");
writer.printType(Type.tClass writer.printType(Type.tClass(clazz));
(ClassInfo.forName(outer.outer)));
writer.print(") "); writer.print(") ");
writer.breakOp(); writer.breakOp();
outerExpr.dumpExpression(writer, 700); outerExpr.dumpExpression(writer, 700);
@ -935,7 +966,7 @@ public final class InvokeOperator extends Operator
&& paramTypes[0].equals(classType)) { && paramTypes[0].equals(classType)) {
writer.print("new "); writer.print("new ");
if (qualifiedNew) if (qualifiedNew)
writer.print(outer.name); writer.print(clazz.getClassName());
else else
writer.printType(Type.tClass(clazz)); writer.printType(Type.tClass(clazz));
break; break;
@ -973,7 +1004,7 @@ public final class InvokeOperator extends Operator
* class, as long as ACC_SUPER is set. * class, as long as ACC_SUPER is set.
*/ */
writer.print("super"); writer.print("super");
ClassInfo superClazz = getClassInfo().getSuperclass(); ClassInfo superClazz = classInfo.getSuperclass();
paramTypes[0] = superClazz == null paramTypes[0] = superClazz == null
? Type.tObject : Type.tClass(superClazz); ? Type.tObject : Type.tClass(superClazz);
writer.breakOp(); writer.breakOp();
@ -1039,7 +1070,7 @@ public final class InvokeOperator extends Operator
case STATIC: { case STATIC: {
arg = 0; arg = 0;
Scope scope = writer.getScope(getClassInfo(), Scope scope = writer.getScope(classInfo,
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (scope == null if (scope == null
||writer.conflicts(methodName, scope, Scope.METHODNAME)) { ||writer.conflicts(methodName, scope, Scope.METHODNAME)) {
@ -1100,8 +1131,11 @@ public final class InvokeOperator extends Operator
} }
writer.print(methodName); writer.print(methodName);
} }
writer.endOp(); writer.endOp();
/* No the easier part: Dump the arguments from arg to length.
* We still need to check for casts though.
*/
writer.breakOp(); writer.breakOp();
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0); writer.startOp(writer.EXPL_PAREN, 0);
@ -1131,10 +1165,10 @@ public final class InvokeOperator extends Operator
writer.endOp(); writer.endOp();
writer.print(")"); writer.print(")");
/* If this was an anonymous constructor call, we must now
* dump the source code of the anonymous class.
*/
if (anonymousNew) { if (anonymousNew) {
/* If this was an anonymous constructor call, we must now
* dump the source code of the anonymous class.
*/
Object state = writer.saveOps(); Object state = writer.saveOps();
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();

@ -23,7 +23,9 @@ import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public abstract class LocalVarOperator extends Operator { public abstract class LocalVarOperator extends Operator {
LocalInfo local; LocalInfo local;
@ -49,6 +51,14 @@ public abstract class LocalVarOperator extends Operator {
updateParentType(local.getType()); updateParentType(local.getType());
} }
public void fillInGenSet(Collection in, Collection gen) {
if (isRead() && in != null)
in.add(getLocalInfo());
if (gen != null)
gen.add(getLocalInfo());
super.fillInGenSet(in, gen);
}
public void fillDeclarables(Collection used) { public void fillDeclarables(Collection used) {
used.add(local); used.add(local);
super.fillDeclarables(used); super.fillDeclarables(used);

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -22,8 +22,10 @@ import jode.type.Type;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collection;
import java.util.Set;
///#enddef
public abstract class Operator extends Expression { public abstract class Operator extends Expression {
/* Don't reorder these constants unless you know what you are doing! */ /* Don't reorder these constants unless you know what you are doing! */
@ -161,13 +163,6 @@ public abstract class Operator extends Expression {
} }
public void fillInGenSet(Collection in, Collection gen) { public void fillInGenSet(Collection in, Collection gen) {
if (this instanceof LocalVarOperator) {
LocalVarOperator varOp = (LocalVarOperator) this;
if (varOp.isRead() && in != null)
in.add(varOp.getLocalInfo());
if (gen != null)
gen.add(varOp.getLocalInfo());
}
for (int i=0; i< subExpressions.length; i++) for (int i=0; i< subExpressions.length; i++)
subExpressions[i].fillInGenSet(in,gen); subExpressions[i].fillInGenSet(in,gen);
} }

@ -54,13 +54,13 @@ public class CaseBlock extends StructuredBlock {
boolean isLastBlock = false; boolean isLastBlock = false;
public CaseBlock(int value) { public CaseBlock(int value) {
this(false);
this.value = value; this.value = value;
subBlock = null;
} }
public CaseBlock(int value, Jump dest) { public CaseBlock(boolean isDef) {
this.value = value; isDefault = isDef;
subBlock = new EmptyBlock(dest); subBlock = new EmptyBlock();
subBlock.outer = this; subBlock.outer = this;
} }
@ -92,7 +92,7 @@ public class CaseBlock extends StructuredBlock {
*/ */
protected boolean wantBraces() { protected boolean wantBraces() {
StructuredBlock block = subBlock; StructuredBlock block = subBlock;
if (block == null) if (block instanceof EmptyBlock)
return false; return false;
for (;;) { for (;;) {
if (block.declare != null && !block.declare.isEmpty()) { if (block.declare != null && !block.declare.isEmpty()) {
@ -132,9 +132,7 @@ public class CaseBlock extends StructuredBlock {
* Returns all sub block of this structured block. * Returns all sub block of this structured block.
*/ */
public StructuredBlock[] getSubBlocks() { public StructuredBlock[] getSubBlocks() {
return (subBlock != null) return new StructuredBlock[] { subBlock };
? new StructuredBlock[] { subBlock }
: new StructuredBlock[0];
} }
public void dumpInstruction(jode.decompiler.TabbedPrintWriter writer) public void dumpInstruction(jode.decompiler.TabbedPrintWriter writer)
@ -177,7 +175,10 @@ public class CaseBlock extends StructuredBlock {
constOp.makeInitializer(); constOp.makeInitializer();
writer.print("case " + constOp.toString() + ":"); writer.print("case " + constOp.toString() + ":");
} }
if (subBlock != null) { if (subBlock instanceof EmptyBlock
&& subBlock.jump == null) {
writer.println();
} else {
boolean needBraces = wantBraces(); boolean needBraces = wantBraces();
if (needBraces) if (needBraces)
writer.openBrace(); writer.openBrace();
@ -190,8 +191,7 @@ public class CaseBlock extends StructuredBlock {
} }
if (needBraces) if (needBraces)
writer.closeBrace(); writer.closeBrace();
} else }
writer.println();
} }
/** /**

@ -26,8 +26,10 @@ import jode.expr.LocalStoreOperator;
import jode.expr.StoreInstruction; import jode.expr.StoreInstruction;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collections;
import java.util.Set;
///#enddef
/** /**

@ -36,23 +36,10 @@ public class ConditionalBlock extends InstructionContainer {
public void checkConsistent() { public void checkConsistent() {
super.checkConsistent(); super.checkConsistent();
if (trueBlock.jump == null if (!(trueBlock instanceof EmptyBlock))
|| !(trueBlock instanceof EmptyBlock))
throw new jode.AssertError("Inconsistency"); throw new jode.AssertError("Inconsistency");
} }
/**
* Creates a new if conditional block.
*/
public ConditionalBlock(Expression cond, Jump condJump, Jump elseJump) {
super(cond, elseJump);
/* cond is a CompareBinary or CompareUnary operator, so no
* check for LocalVarOperator (for condJump) is needed here.
*/
trueBlock = new EmptyBlock(condJump);
trueBlock.outer = this;
}
/** /**
* Creates a new if conditional block. * Creates a new if conditional block.
*/ */
@ -69,6 +56,19 @@ public class ConditionalBlock extends InstructionContainer {
* implementation * implementation
*/ */
/**
* Sets the successors of this structured block. This should be only
* called once, by FlowBlock.setSuccessors().
*/
public void setSuccessors(Jump[] jumps) {
if (jumps.length != 2) {
/* A conditional block can only exactly two jumps. */
throw new IllegalArgumentException("Not exactly two jumps.");
}
trueBlock.setJump(jumps[0]);
setJump(jumps[1]);
}
/** /**
* Returns all sub block of this structured block. * Returns all sub block of this structured block.
*/ */

@ -82,7 +82,6 @@ public class CreateCheckNull {
* DUP * DUP
* if (POP == null) { * if (POP == null) {
* throw null * throw null
* GOTO END_OF_METHOD // not checked
* } * }
* </pre> * </pre>
* to a CheckNullOperator. This is what jikes generates when it * to a CheckNullOperator. This is what jikes generates when it
@ -111,7 +110,6 @@ public class CreateCheckNull {
LocalInfo li = new LocalInfo(); LocalInfo li = new LocalInfo();
InstructionContainer ic = InstructionContainer ic =
new InstructionBlock(new CheckNullOperator(Type.tUObject, li)); new InstructionBlock(new CheckNullOperator(Type.tUObject, li));
ifBlock.flowBlock.removeSuccessor(ifBlock.thenBlock.jump);
ic.moveJump(ifBlock.jump); ic.moveJump(ifBlock.jump);
if (last == ifBlock) { if (last == ifBlock) {
ic.replace(last.outer); ic.replace(last.outer);

@ -63,10 +63,11 @@ public class CreateClassField {
return false; return false;
InvokeOperator invoke = (InvokeOperator) store.getSubExpressions()[1]; InvokeOperator invoke = (InvokeOperator) store.getSubExpressions()[1];
Expression param = invoke.getSubExpressions()[0]; if (!invoke.isGetClass())
return false;
if (invoke.isGetClass() Expression param = invoke.getSubExpressions()[0];
&& param instanceof ConstOperator if (param instanceof ConstOperator
&& ((ConstOperator)param).getValue() instanceof String) { && ((ConstOperator)param).getValue() instanceof String) {
String clazz = (String) ((ConstOperator)param).getValue(); String clazz = (String) ((ConstOperator)param).getValue();
if (put.getField().setClassConstant(clazz)) { if (put.getField().setClassConstant(clazz)) {

@ -28,11 +28,13 @@ import jode.expr.CombineableOperator;
import jode.util.SimpleMap; import jode.util.SimpleMap;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Map;
import @COLLECTIONS@.Set; import java.util.Iterator;
import @COLLECTIONS@.ArrayList; import java.util.Set;
import @COLLECTIONS@.List; import java.util.ArrayList;
import java.util.List;
///#enddef
/** /**
* A flow block is the structure of which the flow graph consists. A * A flow block is the structure of which the flow graph consists. A
@ -49,16 +51,15 @@ import @COLLECTIONS@.List;
public class FlowBlock { public class FlowBlock {
public static FlowBlock END_OF_METHOD; public static FlowBlock END_OF_METHOD;
public static FlowBlock NEXT_BY_ADDR; // public static FlowBlock NEXT_BY_ADDR;
static { static {
END_OF_METHOD = new FlowBlock(null, Integer.MAX_VALUE); END_OF_METHOD = new FlowBlock(null, Integer.MAX_VALUE, null);
END_OF_METHOD.appendBlock(new EmptyBlock(), 0);
END_OF_METHOD.label = "END_OF_METHOD"; END_OF_METHOD.label = "END_OF_METHOD";
NEXT_BY_ADDR = new FlowBlock(null, -1); // NEXT_BY_ADDR = new FlowBlock(null, -1);
NEXT_BY_ADDR.appendBlock(new DescriptionBlock("FALL THROUGH"), 0); // NEXT_BY_ADDR.appendBlock(new DescriptionBlock("FALL THROUGH"), 0);
NEXT_BY_ADDR.label = "NEXT_BY_ADDR"; // NEXT_BY_ADDR.label = "NEXT_BY_ADDR";
} }
/** /**
@ -75,21 +76,33 @@ public class FlowBlock {
* uses that variable, on which it is never assigned * uses that variable, on which it is never assigned
*/ */
private SlotSet in = new SlotSet(); private SlotSet in = new SlotSet();
/**
* The gen locals. This are the locals, to which are written
* somewhere in this flow block. This is only used for try
* catch blocks.
*/
VariableSet used = new VariableSet();
/** /**
* The gen locals. This are the locals, to which are written * The gen locals. This are the locals, to which are written
* somewhere in this flow block. This is only used for try * somewhere in this flow block. This is only used for try
* catch blocks. * catch blocks.
*/ */
VariableSet gen = new VariableSet(); VariableSet gen = new VariableSet();
/**
* The gen locals. This are the locals, to which are written
* somewhere in this flow block. This is only used for try
* catch blocks.
*/
SlotSet kill = new SlotSet();
/** /**
* The starting address of this flow block. This is mainly used * The starting blockNr of this flow block. This is mainly used
* to produce the source code in code order. * to produce the source code in code order.
*/ */
private int addr; private int blockNr;
/** /**
* The length of the structured block, only needed at the beginning. * The number of flow blocks that were combined into this block so far.
*/ */
private int length; private int length;
@ -127,13 +140,13 @@ public class FlowBlock {
* This is a pointer to the next flow block in byte code order. * This is a pointer to the next flow block in byte code order.
* It is null for the last flow block. * It is null for the last flow block.
*/ */
FlowBlock nextByAddr; FlowBlock nextByCodeOrder;
/** /**
* This is a pointer to the previous flow block in byte code order. * This is a pointer to the previous flow block in byte code order.
* It is null for the first flow block. * It is null for the first flow block.
*/ */
FlowBlock prevByAddr; FlowBlock prevByCodeOrder;
/** /**
* The stack map. This tells how many objects are on stack at * The stack map. This tells how many objects are on stack at
@ -168,17 +181,24 @@ public class FlowBlock {
Jump jumps; Jump jumps;
} }
/** /**
* The default constructor. Creates a new empty flowblock. * The default constructor. Creates a new empty flowblock.
*/ */
public FlowBlock(MethodAnalyzer method, int addr) { public FlowBlock(MethodAnalyzer method, int blockNr, FlowBlock lastFlow) {
this.method = method; this.method = method;
this.addr = addr; this.blockNr = blockNr;
length = 1;
prevByCodeOrder = lastFlow;
if (lastFlow != null)
lastFlow.nextByCodeOrder = this;
block = new EmptyBlock();
block.setFlowBlock(this);
lastModified = block;
} }
public final int getNextAddr() { public int getNextBlockNr() {
return addr+length; return blockNr + length;
} }
public boolean hasNoJumps() { public boolean hasNoJumps() {
@ -379,9 +399,22 @@ public class FlowBlock {
} }
newIfBlock.moveJump(jump); newIfBlock.moveJump(jump);
/* consider this jump again */ // /* consider this jump again */
jumps = jump; // jumps = jump;
continue; /* Consider all jumps again, since the ones that moved
* into the thenBlock may be obsolete now.
* XXX only jumps in then should be considered.
*/
if (remainingJumps == null)
jumps = jump;
else {
jumps = remainingJumps;
while (remainingJumps.next != null)
remainingJumps = remainingJumps.next;
remainingJumps.next = jump;
remainingJumps = null;
}
continue;
} }
} else { } else {
@ -394,19 +427,37 @@ public class FlowBlock {
} }
/* Now find the real outer block, that is ascend the chain /* Now find the real outer block, that is ascend the
* of SequentialBlocks. * chain of SequentialBlocks.
* *
* Note that only the last instr in a SequentialBlock chain * Note that only the last instr in a SequentialBlock chain
* can have a jump. * can have a jump.
* *
* We rely on the fact, that instanceof returns false * We rely on the fact, that instanceof returns false
* for a null pointer. * for a null pointer.
*/ */
StructuredBlock sb = jump.prev.outer; StructuredBlock sb = jump.prev.outer;
while (sb instanceof SequentialBlock) while (sb instanceof SequentialBlock)
sb = sb.outer; sb = sb.outer;
/* If the block is a catch, go up to the try block.
*/
if (sb instanceof CatchBlock
&& sb.jumpMayBeChanged())
sb = sb.outer;
/* If the block is a synchronized or try block
* and the jump may be changed, move the jump up.
*/
if ((sb instanceof CatchBlock
|| sb instanceof SynchronizedBlock
|| sb instanceof TryBlock)
&& sb.jumpMayBeChanged()) {
sb.moveJump(jump);
/* consider this jump again */
jumps = jump;
continue;
}
/* if this is an unconditional jump at the end of a /* if this is an unconditional jump at the end of a
* then block belonging to a if-then block without * then block belonging to a if-then block without
@ -461,9 +512,22 @@ public class FlowBlock {
lastModified = ifBlock; lastModified = ifBlock;
} }
/* Consider all jumps again, since the ones that moved
* into the thenBlock may be obsolete now.
* XXX only jumps in then should be considered.
*/
if (remainingJumps == null)
jumps = jump;
else {
jumps = remainingJumps;
while (remainingJumps.next != null)
remainingJumps = remainingJumps.next;
remainingJumps.next = jump;
remainingJumps = null;
}
/* consider this jump again */ /* consider this jump again */
ifBlock.moveJump(jump); // ifBlock.moveJump(jump);
jumps = jump; // jumps = jump;
continue; continue;
} }
} }
@ -601,67 +665,76 @@ public class FlowBlock {
} }
/** /**
* Fixes the addr chained list, after merging this block with succ. * Fixes the blockNr chained list, after merging this block with succ.
*/ */
public void mergeAddr(FlowBlock succ) { public void mergeBlockNr(FlowBlock succ) {
if (succ.nextByAddr == this || succ.prevByAddr == null) { if (succ.nextByCodeOrder == this || succ.prevByCodeOrder == null) {
/* Merge succ with its nextByAddr. /* Merge succ with its nextByCodeOrder.
* Note: succ.nextByAddr != null, since this is on the * Note: succ.nextByCodeOrder != null, since this is on the
* nextByAddr chain. */ * nextByCodeOrder chain. */
succ.nextByAddr.addr = succ.addr; succ.nextByCodeOrder.blockNr = succ.blockNr;
succ.nextByAddr.length += succ.length; succ.nextByCodeOrder.length += succ.length;
succ.nextByAddr.prevByAddr = succ.prevByAddr; succ.nextByCodeOrder.prevByCodeOrder = succ.prevByCodeOrder;
if (succ.prevByAddr != null) if (succ.prevByCodeOrder != null)
succ.prevByAddr.nextByAddr = succ.nextByAddr; succ.prevByCodeOrder.nextByCodeOrder = succ.nextByCodeOrder;
} else { } else {
/* Merge succ with its prevByAddr */ /* Merge succ with its prevByCodeOrder */
succ.prevByAddr.length += succ.length; succ.prevByCodeOrder.length += succ.length;
succ.prevByAddr.nextByAddr = succ.nextByAddr; succ.prevByCodeOrder.nextByCodeOrder = succ.nextByCodeOrder;
if (succ.nextByAddr != null) if (succ.nextByCodeOrder != null)
succ.nextByAddr.prevByAddr = succ.prevByAddr; succ.nextByCodeOrder.prevByCodeOrder = succ.prevByCodeOrder;
} }
} }
/**
* Updates the gen/kill Sets of all jumps in this block.
* @param gens The locals in this block that are visible at the
* begin of successor.
* @param kills The slots that are always overwritten on the way to
* successor. This may be null.
* @return The variables that must be defined * in this block.
*/
void updateGenKill(VariableSet gens, SlotSet kills) {
/* Merge the locals used in successing block with those written
* by this blocks.
*/
in.merge(gens);
/* The gen/kill sets must be updated for every jump
* in the other block */
Iterator i = successors.values().iterator();
while (i.hasNext()) {
SuccessorInfo succInfo = (SuccessorInfo) i.next();
succInfo.gen.mergeGenKill(gens, succInfo.kill);
if (kills != null)
succInfo.kill.mergeKill(kills);
}
}
/** /**
* Updates the in/out-Vectors of the structured block of the * Updates the in/out-Vectors of the structured block of the
* successing flow block simultanous to a T2 transformation. * successing flow block simultanous to a T2 transformation.
* @param successor The flow block which is unified with this flow * @param successor The flow block which is unified with this flow
* block. * block.
* @param jumps The list of jumps to successor in this block. * @param gens The locals in this block that are visible at the
* @return The variables that must be defined in this block. * begin of successor.
* @param kills The slots that are always overwritten on the way to
* successor.
* @return The variables that must be defined * in this block.
*/ */
void updateInOut(FlowBlock successor, SuccessorInfo succInfo) { void updateInOut(FlowBlock successor, VariableSet gens, SlotSet kills) {
/* First get the gen/kill sets of all jumps to successor and successor.updateGenKill(gens, kills);
* calculate the intersection.
*/
SlotSet kills = succInfo.kill;
VariableSet gens = succInfo.gen;
/* Merge the locals used in successing block with those written
* by this blocks.
*/
successor.in.merge(gens);
/* The ins of the successor that are not killed /* The ins of the successor that are not killed
* (i.e. unconditionally overwritten) by this block are new * (i.e. unconditionally overwritten) by this block are new
* ins for this block. * ins for this block.
*/ */
SlotSet newIn = (SlotSet) successor.in.clone(); SlotSet newIn = (SlotSet) successor.in.clone();
newIn.removeAll(kills); newIn.removeAll(kills);
/* The gen/kill sets must be updated for every jump
* in the other block */
Iterator i = successor.successors.values().iterator();
while (i.hasNext()) {
SuccessorInfo succSuccInfo = (SuccessorInfo) i.next();
succSuccInfo.gen.mergeGenKill(gens, succSuccInfo.kill);
if (successor != this)
succSuccInfo.kill.mergeKill(kills);
}
this.in.addAll(newIn); this.in.addAll(newIn);
this.gen.addAll(successor.gen); this.used.addAll(successor.used);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) {
GlobalOptions.err.println("UpdateInOut: gens : "+gens); GlobalOptions.err.println("UpdateInOut: gens : "+gens);
@ -708,7 +781,7 @@ public class FlowBlock {
succSuccInfo.gen.mergeGenKill(gens, succSuccInfo.kill); succSuccInfo.gen.mergeGenKill(gens, succSuccInfo.kill);
} }
in.addAll(catchFlow.in); in.addAll(catchFlow.in);
gen.addAll(catchFlow.gen); used.addAll(catchFlow.used);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) {
GlobalOptions.err.println("UpdateInOutCatch: gens : "+gens); GlobalOptions.err.println("UpdateInOutCatch: gens : "+gens);
@ -795,95 +868,110 @@ public class FlowBlock {
} }
} }
public void appendBlock(StructuredBlock block, int length) { public void prependBlock(StructuredBlock insertBlock) {
SlotSet succIn = new SlotSet(); lastModified = insertBlock.appendBlock(block);
SlotSet succKill = new SlotSet(); SlotSet blockIn = new SlotSet();
VariableSet succGen = new VariableSet(); SlotSet blockKill = new SlotSet();
block.fillInGenSet(succIn, succKill); VariableSet blockGen = new VariableSet();
succGen.addAll(succKill);
insertBlock.fillInGenSet(blockIn, blockKill);
if (this.block == null) { blockGen.addAll(blockKill);
this.block = block;
lastModified = block; updateGenKill(blockGen, blockKill);
block.setFlowBlock(this); in.removeAll(blockKill);
block.fillSuccessors(); in.addAll(blockIn);
this.length = length; used.addAll(blockGen);
in = succIn; doTransformations();
gen = succGen; checkConsistent();
for (Iterator i = successors.values().iterator(); i.hasNext();) { }
SuccessorInfo info = (SuccessorInfo) i.next();
info.gen = new VariableSet(); public void appendReadBlock(StructuredBlock newBlock, LocalInfo local) {
info.kill = new SlotSet(); used.add(local);
info.gen.addAll(succGen); if (!kill.contains(local))
info.kill.addAll(succKill); in.add(local);
} gen.mergeRead(local);
} else if (!(block instanceof EmptyBlock)) { kill.mergeKill(local);
checkConsistent();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_FLOW) != 0) {
GlobalOptions.err.println("appending Block: "+block);
}
SuccessorInfo succInfo newBlock.setFlowBlock(this);
= (SuccessorInfo) successors.get(NEXT_BY_ADDR); lastModified = lastModified.appendBlock(newBlock);
succIn.merge(succInfo.gen); doTransformations();
succIn.removeAll(succInfo.kill);
succGen.mergeGenKill(succInfo.gen, succKill);
succKill.mergeKill(succInfo.kill);
this.in.addAll(succIn);
this.gen.addAll(succKill);
removeSuccessor(lastModified.jump);
lastModified.removeJump();
lastModified = lastModified.appendBlock(block);
block.fillSuccessors();
succInfo = (SuccessorInfo) successors.get(NEXT_BY_ADDR);
succInfo.gen = succGen;
succInfo.kill = succKill;
this.length += length;
checkConsistent();
doTransformations();
}
checkConsistent(); checkConsistent();
} }
/** public void appendWriteBlock(StructuredBlock newBlock, LocalInfo local) {
* Append the given flowblock to the nextByAddr/prevByAddr chain. used.add(local);
* nextByAddr should be null, when calling this. gen.mergeWrite(local);
* @param flow The flowBlock to append kill.mergeKill(local);
*/
public void setNextByAddr(FlowBlock flow) newBlock.setFlowBlock(this);
{ lastModified = lastModified.appendBlock(newBlock);
/* nextByAddr can be set, when reordering block in transform exc */ doTransformations();
// if (nextByAddr != null) checkConsistent();
// throw new IllegalStateException("nextByAddr already set"); }
if (flow == END_OF_METHOD || flow == NEXT_BY_ADDR)
throw new IllegalArgumentException public void appendBlock(StructuredBlock newBlock) {
("nextByAddr mustn't be special"); newBlock.setFlowBlock(this);
SuccessorInfo info = (SuccessorInfo) successors.remove(NEXT_BY_ADDR); lastModified = lastModified.appendBlock(newBlock);
SuccessorInfo flowInfo = (SuccessorInfo) successors.get(flow); doTransformations();
if (info != null) { checkConsistent();
NEXT_BY_ADDR.predecessors.remove(this); }
Jump jumps = info.jumps;
jumps.destination = flow; public void oldAppendBlock(StructuredBlock newBlock) {
while (jumps.next != null) { SlotSet blockIn = new SlotSet();
jumps = jumps.next; SlotSet blockKill = new SlotSet();
jumps.destination = flow; VariableSet blockGen = new VariableSet();
} newBlock.setFlowBlock(this);
successors.put(flow, info); newBlock.fillInGenSet(blockIn, blockKill);
if (flowInfo != null) { this.used.addAll(blockKill);
info.gen.addAll(flowInfo.gen); blockGen.addAll(blockKill);
info.kill.retainAll(flowInfo.kill);
jumps.next = flowInfo.jumps; /* Merge the locals used in new block with those written
} else * by this blocks.
flow.predecessors.add(this); */
} blockIn.merge(this.gen);
blockGen.mergeGenKill(this.gen, blockKill);
blockKill.mergeKill(this.kill);
blockIn.removeAll(this.kill);
this.in.addAll(blockIn);
this.gen = blockGen;
this.kill = blockKill;
lastModified = lastModified.appendBlock(newBlock);
checkConsistent();
doTransformations();
checkConsistent(); checkConsistent();
}
nextByAddr = flow; public void setSuccessors(FlowBlock[] succs) {
flow.prevByAddr = this; SlotSet blockIn = new SlotSet();
SlotSet blockKill = new SlotSet();
VariableSet blockGen = new VariableSet();
Jump[] jumps = new Jump[succs.length];
for (int i=0; i< succs.length; i++) {
Jump jump = new Jump(succs[i]);
SuccessorInfo info = (SuccessorInfo) successors.get(succs[i]);
if (info == null) {
info = new SuccessorInfo();
info.gen = (VariableSet) gen.clone();
info.kill = (SlotSet) kill.clone();
info.jumps = jump;
if (jump.destination != END_OF_METHOD)
jump.destination.predecessors.add(this);
successors.put(succs[i], info);
} else {
jump.next = info.jumps;
info.jumps = jump;
}
jumps[i] = jump;
}
if (jumps.length > 0)
lastModified.setSuccessors(jumps);
gen = null;
kill = null;
checkConsistent();
} }
/** /**
@ -905,13 +993,13 @@ public class FlowBlock {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("T2(["+addr+","+getNextAddr()+"],[" ("T2(["+blockNr+","+getNextBlockNr()+"],["
+succ.addr+","+succ.getNextAddr()+"])"); +succ.blockNr+","+succ.getNextBlockNr()+"])");
SuccessorInfo succInfo = (SuccessorInfo) successors.remove(succ); SuccessorInfo succInfo = (SuccessorInfo) successors.remove(succ);
/* Update the in/out-Vectors now */ /* Update the in/out-Vectors now */
updateInOut(succ, succInfo); updateInOut(succ, succInfo.gen, succInfo.kill);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0) if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before Resolve: "+this); GlobalOptions.err.println("before Resolve: "+this);
@ -932,8 +1020,8 @@ public class FlowBlock {
/* This will also set last modified to the new correct value. */ /* This will also set last modified to the new correct value. */
doTransformations(); doTransformations();
/* Set addr and length to correct value and update nextByAddr */ /* Set blockNr and length to correct value and update nextByCodeOrder */
mergeAddr(succ); mergeBlockNr(succ);
/* T2 transformation succeeded */ /* T2 transformation succeeded */
checkConsistent(); checkConsistent();
@ -968,10 +1056,16 @@ public class FlowBlock {
jumps = jump; jumps = jump;
} }
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before resolve: "+this);
/* Try to eliminate as many jumps as possible. /* Try to eliminate as many jumps as possible.
*/ */
jumps = resolveSomeJumps(jumps, END_OF_METHOD); jumps = resolveSomeJumps(jumps, END_OF_METHOD);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before remaining: "+this);
next_jump: next_jump:
for (; jumps != null; jumps = jumps.next) { for (; jumps != null; jumps = jumps.next) {
@ -1011,7 +1105,14 @@ public class FlowBlock {
if (lastModified.jump.destination == END_OF_METHOD) if (lastModified.jump.destination == END_OF_METHOD)
lastModified.removeJump(); lastModified.removeJump();
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before Transformation: "+this);
doTransformations(); doTransformations();
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("after Transformation: "+this);
/* transformation succeeded */ /* transformation succeeded */
checkConsistent(); checkConsistent();
} }
@ -1019,7 +1120,7 @@ public class FlowBlock {
public boolean doT1(int start, int end) { public boolean doT1(int start, int end) {
/* If there are no jumps to the beginning of this flow block /* If there are no jumps to the beginning of this flow block
* or if this block has other predecessors with a not yet * or if this block has other predecessors with a not yet
* considered address, return false. The second condition * considered block number, return false. The second condition
* make sure that not for each continue a while is created. * make sure that not for each continue a while is created.
*/ */
if (!predecessors.contains(this)) if (!predecessors.contains(this))
@ -1027,7 +1128,7 @@ public class FlowBlock {
for (Iterator i = predecessors.iterator(); i.hasNext(); ) { for (Iterator i = predecessors.iterator(); i.hasNext(); ) {
FlowBlock predFlow = (FlowBlock) i.next(); FlowBlock predFlow = (FlowBlock) i.next();
if (predFlow != null && predFlow != this if (predFlow != null && predFlow != this
&& predFlow.addr >= start && predFlow.addr < end) { && predFlow.blockNr >= start && predFlow.blockNr < end) {
return false; return false;
} }
} }
@ -1035,11 +1136,11 @@ public class FlowBlock {
checkConsistent(); checkConsistent();
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println("T1(["+addr+","+getNextAddr()+"])"); GlobalOptions.err.println("T1(["+blockNr+","+getNextBlockNr()+"])");
SuccessorInfo succInfo = (SuccessorInfo) successors.remove(this); SuccessorInfo succInfo = (SuccessorInfo) successors.remove(this);
/* Update the in/out-Vectors now */ /* Update the in/out-Vectors now */
updateInOut(this, succInfo); updateGenKill(succInfo.gen, null);
Jump jumps = succInfo.jumps; Jump jumps = succInfo.jumps;
StructuredBlock bodyBlock = block; StructuredBlock bodyBlock = block;
@ -1174,8 +1275,14 @@ public class FlowBlock {
*/ */
predecessors.remove(this); predecessors.remove(this);
lastModified = block; lastModified = block;
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before Transformation: "+this);
doTransformations(); doTransformations();
// mergeCondition();
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("after Transformation: "+this);
/* T1 analysis succeeded */ /* T1 analysis succeeded */
checkConsistent(); checkConsistent();
@ -1184,9 +1291,6 @@ public class FlowBlock {
} }
public void doTransformations() { public void doTransformations() {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before Transformation: "+this);
while (lastModified instanceof SequentialBlock) { while (lastModified instanceof SequentialBlock) {
if (lastModified.getSubBlocks()[0].doTransformations()) if (lastModified.getSubBlocks()[0].doTransformations())
continue; continue;
@ -1194,28 +1298,25 @@ public class FlowBlock {
} }
while (lastModified.doTransformations()) while (lastModified.doTransformations())
{ /* empty */ } { /* empty */ }
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("after Transformation: "+this);
} }
/** /**
* Search for an apropriate successor. * Search for an apropriate successor.
* @param prevSucc The successor, that was previously tried. * @param prevSucc The successor, that was previously tried.
* @param start The minimum address. * @param start The minimum blockNr
* @param end The maximum address + 1. * @param end The maximum blockNr + 1.
* @return the successor with smallest address greater than prevSucc * @return the successor with smallest block number greater than prevSucc
* or null if there isn't any further successor at all. * or null if there isn't any further successor at all.
*/ */
FlowBlock getSuccessor(int start, int end) { FlowBlock getSuccessor(int start, int end) {
/* search successor with smallest addr. */ /* search successor with smallest blockNr. */
Iterator keys = successors.keySet().iterator(); Iterator keys = successors.keySet().iterator();
FlowBlock succ = null; FlowBlock succ = null;
while (keys.hasNext()) { while (keys.hasNext()) {
FlowBlock fb = (FlowBlock) keys.next(); FlowBlock fb = (FlowBlock) keys.next();
if (fb.addr < start || fb.addr >= end || fb == this) if (fb.blockNr < start || fb.blockNr >= end || fb == this)
continue; continue;
if (succ == null || fb.addr < succ.addr) { if (succ == null || fb.blockNr < succ.blockNr) {
succ = fb; succ = fb;
} }
} }
@ -1228,16 +1329,17 @@ public class FlowBlock {
* block. * block.
*/ */
public void analyze() { public void analyze() {
analyze(0, Integer.MAX_VALUE); while (analyze(0, Integer.MAX_VALUE))
{ }
mergeEndBlock(); mergeEndBlock();
} }
/** /**
* The main analyzation. This calls doT1 and doT2 on apropriate * The main analyzation. This calls doT1 and doT2 on apropriate
* regions. Only blocks whose address lies in the given address * regions. Only blocks whose block number lies in the given block number
* range are considered. * range are considered.
* @param start the start of the address range. * @param start the start of the block number range.
* @param end the end of the address range. * @param end the end of the block number range.
*/ */
public boolean analyze(int start, int end) { public boolean analyze(int start, int end) {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0)
@ -1251,7 +1353,7 @@ public class FlowBlock {
if (lastModified instanceof SwitchBlock) { if (lastModified instanceof SwitchBlock) {
/* analyze the switch first. /* analyze the switch first.
*/ */
analyzeSwitch(start, end); changed |= analyzeSwitch(start, end);
} }
@ -1264,8 +1366,7 @@ public class FlowBlock {
* make another T2 analysis in the previous * make another T2 analysis in the previous
* block possible. * block possible.
*/ */
if (addr != 0) return true;
return true;
} }
FlowBlock succ = getSuccessor(start, end); FlowBlock succ = getSuccessor(start, end);
@ -1279,68 +1380,71 @@ public class FlowBlock {
GlobalOptions.err.println GlobalOptions.err.println
("No more successors applicable: " ("No more successors applicable: "
+ start + " - " + end + "; " + start + " - " + end + "; "
+ addr + " - " + getNextAddr()); + blockNr + " - " + getNextBlockNr());
return changed; return changed;
} else { } else if ((nextByCodeOrder == succ
if ((nextByAddr == succ || succ.nextByAddr == this) || succ.nextByCodeOrder == this)
/* Only do T2 transformation if the blocks are /* Only do T2 transformation if the blocks are
* adjacent. * adjacent.
*/ */
&& doT2(succ)) { && doT2(succ)) {
/* T2 transformation succeeded. */ /* T2 transformation succeeded. */
changed = true; changed = true;
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_FLOW) != 0) & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("after T2: "+this); GlobalOptions.err.println("after T2: "+this);
break; break;
}
/* Check if all predecessors of succ } else {
* lie in range [start,end). Otherwise
* we have no chance to combine succ /* Check if all predecessors of either succ or this
* block lie in range [start,end). Otherwise
* we have no chance to combine these two blocks.
*/ */
boolean predOutOfRange = false;
for (Iterator i = succ.predecessors.iterator(); for (Iterator i = succ.predecessors.iterator();
i.hasNext(); ) { i.hasNext(); ) {
int predAddr = ((FlowBlock)i.next()).addr; int predBlockNr = ((FlowBlock)i.next()).blockNr;
if (predAddr < start || predAddr >= end) { if (predBlockNr < start || predBlockNr >= end) {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println
("breaking analyze(" GlobalOptions.err.println
+ start + ", " + end + "); " ("breaking analyze("
+ addr + " - " + getNextAddr()); + start + ", " + end + "); "
return changed; + blockNr + " - " + getNextBlockNr());
return changed;
} }
} }
/* analyze succ, the new region is the /* analyze succ, the new region is the
* continuous region of * continuous region of
* [start,end) \cap \compl [addr, getNextAddr()) * [start,end) \cap \compl [blockNr, getNextBlockNr())
* where succ.addr lies in. * where succ.blockNr lies in.
*/ */
int newStart = (succ.addr > addr) int newStart = (succ.blockNr > blockNr)
? getNextAddr() : start; ? getNextBlockNr() : start;
int newEnd = (succ.addr > addr) int newEnd = (succ.blockNr > blockNr)
? end : addr; ? end : blockNr;
if (succ.analyze(newStart, newEnd)) if (succ.analyze(newStart, newEnd))
break; break;
} }
/* Try the next successor. /* Try the next successor.
*/ */
succ = getSuccessor(succ.addr+1, end); succ = getSuccessor(succ.blockNr+1, end);
} }
} }
} }
/** /**
* The switch analyzation. This calls doSwitchT2 and doT1 on apropriate * The switch analyzation. This calls doSwitchT2 and doT1 on apropriate
* regions. Only blocks whose address lies in the given address * regions. Only blocks whose block number lies in the given block number
* range are considered and it is taken care of, that the switch * range are considered and it is taken care of, that the switch
* is never leaved. <p> * is never leaved. <p>
* The current flow block must contain the switch block as lastModified. * The current flow block must contain the switch block as lastModified.
* @param start the start of the address range. * @param start the start of the block number range.
* @param end the end of the address range. * @param end the end of the block number range.
*/ */
public boolean analyzeSwitch(int start, int end) { public boolean analyzeSwitch(int start, int end) {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0)
@ -1356,18 +1460,18 @@ public class FlowBlock {
&& switchBlock.caseBlocks[i].subBlock.jump != null) { && switchBlock.caseBlocks[i].subBlock.jump != null) {
FlowBlock nextFlow = switchBlock.caseBlocks[i]. FlowBlock nextFlow = switchBlock.caseBlocks[i].
subBlock.jump.destination; subBlock.jump.destination;
if (nextFlow.addr >= end) if (nextFlow.blockNr >= end)
break; break;
else if (nextFlow.addr >= start) { else if (nextFlow.blockNr >= start) {
/* First analyze the nextFlow block. It may /* First analyze the nextFlow block. It may
* return early after a T1 trafo so call it * return early after a T1 trafo so call it
* until nothing more is possible. * until nothing more is possible.
*/ */
while (nextFlow.analyze(getNextAddr(), end)) while (nextFlow.analyze(getNextBlockNr(), end))
changed = true; changed = true;
if (nextFlow.addr != getNextAddr()) if (nextFlow.blockNr != getNextBlockNr())
break; break;
/* Check if nextFlow has only the previous case /* Check if nextFlow has only the previous case
@ -1405,7 +1509,7 @@ public class FlowBlock {
lastFlow.resolveRemaining(lastJumps); lastFlow.resolveRemaining(lastJumps);
switchBlock.caseBlocks[last+1].isFallThrough = true; switchBlock.caseBlocks[last+1].isFallThrough = true;
} }
updateInOut(nextFlow, info); updateInOut(nextFlow, info.gen, info.kill);
if (lastFlow != null) { if (lastFlow != null) {
lastFlow.block.replace lastFlow.block.replace
@ -1418,7 +1522,7 @@ public class FlowBlock {
*/ */
switchBlock.caseBlocks[i].subBlock.removeJump(); switchBlock.caseBlocks[i].subBlock.removeJump();
mergeAddr(nextFlow); mergeBlockNr(nextFlow);
lastFlow = nextFlow; lastFlow = nextFlow;
last = i; last = i;
@ -1440,7 +1544,7 @@ public class FlowBlock {
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeSwitch done: " + start + " - " + end + "; " ("analyzeSwitch done: " + start + " - " + end + "; "
+ addr + " - " + getNextAddr()); + blockNr + " - " + getNextBlockNr());
checkConsistent(); checkConsistent();
return changed; return changed;
} }
@ -1463,7 +1567,7 @@ public class FlowBlock {
} }
if (destJumps == null) if (destJumps == null)
throw new IllegalArgumentException throw new IllegalArgumentException
(addr+": removing non existent jump: " + jump); (blockNr+": removing non existent jump: " + jump);
if (prev != null) if (prev != null)
prev.next = destJumps.next; prev.next = destJumps.next;
@ -1490,19 +1594,19 @@ public class FlowBlock {
return successors.keySet(); return successors.keySet();
} }
public void addSuccessor(Jump jump) { // public void addSuccessor(Jump jump) {
SuccessorInfo info = (SuccessorInfo) successors.get(jump.destination); // SuccessorInfo info = (SuccessorInfo) successors.get(jump.destination);
if (info == null) { // if (info == null) {
info = new SuccessorInfo(); // info = new SuccessorInfo();
info.jumps = jump; // info.jumps = jump;
if (jump.destination != END_OF_METHOD) // if (jump.destination != END_OF_METHOD)
jump.destination.predecessors.add(this); // jump.destination.predecessors.add(this);
successors.put(jump.destination, info); // successors.put(jump.destination, info);
} else { // } else {
jump.next = info.jumps; // jump.next = info.jumps;
info.jumps = jump; // info.jumps = jump;
} // }
} // }
/** /**
* This is called after the analysis is completely done. It * This is called after the analysis is completely done. It
@ -1564,8 +1668,8 @@ public class FlowBlock {
public void removeOnetimeLocals() { public void removeOnetimeLocals() {
block.removeOnetimeLocals(); block.removeOnetimeLocals();
if (nextByAddr != null) if (nextByCodeOrder != null)
nextByAddr.removeOnetimeLocals(); nextByCodeOrder.removeOnetimeLocals();
} }
private void promoteInSets() { private void promoteInSets() {
@ -1594,8 +1698,8 @@ public class FlowBlock {
pred.promoteInSets(); pred.promoteInSets();
} }
if (nextByAddr != null) if (nextByCodeOrder != null)
nextByAddr.promoteInSets(); nextByCodeOrder.promoteInSets();
} }
/** /**
@ -1617,8 +1721,8 @@ public class FlowBlock {
public void makeDeclaration(Set done) { public void makeDeclaration(Set done) {
block.propagateUsage(); block.propagateUsage();
block.makeDeclaration(done); block.makeDeclaration(done);
if (nextByAddr != null) if (nextByCodeOrder != null)
nextByAddr.makeDeclaration(done); nextByCodeOrder.makeDeclaration(done);
} }
/** /**
@ -1626,8 +1730,8 @@ public class FlowBlock {
*/ */
public void simplify() { public void simplify() {
block.simplify(); block.simplify();
if (nextByAddr != null) if (nextByCodeOrder != null)
nextByAddr.simplify(); nextByCodeOrder.simplify();
} }
/** /**
@ -1665,8 +1769,8 @@ public class FlowBlock {
} }
} }
if (nextByAddr != null) if (nextByCodeOrder != null)
nextByAddr.dumpSource(writer); nextByCodeOrder.dumpSource(writer);
} }
/** /**
@ -1680,10 +1784,10 @@ public class FlowBlock {
String label = null; String label = null;
/** /**
* Returns the address, where the code in this flow block starts. * Returns the block number, where the code in this flow block starts.
*/ */
public int getAddr() { public int getBlockNr() {
return addr; return blockNr;
} }
/** /**
@ -1692,7 +1796,7 @@ public class FlowBlock {
*/ */
public String getLabel() { public String getLabel() {
if (label == null) if (label == null)
label = "flow_"+addr+"_"+(serialno++)+"_"; label = "flow_"+blockNr+"_"+(serialno++)+"_";
return label; return label;
} }
@ -1707,7 +1811,7 @@ public class FlowBlock {
try { try {
java.io.StringWriter strw = new java.io.StringWriter(); java.io.StringWriter strw = new java.io.StringWriter();
TabbedPrintWriter writer = new TabbedPrintWriter(strw); TabbedPrintWriter writer = new TabbedPrintWriter(strw);
writer.println(super.toString() + ": "+addr+"-"+(addr+length)); writer.println(super.toString() + ": "+blockNr);
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) {
writer.println("in: "+in); writer.println("in: "+in);
} }
@ -1728,6 +1832,8 @@ public class FlowBlock {
} }
} }
return strw.toString(); return strw.toString();
} catch (RuntimeException ex) {
return super.toString();
} catch (java.io.IOException ex) { } catch (java.io.IOException ex) {
return super.toString(); return super.toString();
} }

@ -24,7 +24,9 @@ import jode.expr.Expression;
import jode.type.Type; import jode.type.Type;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* An IfThenElseBlock is the structured block representing an if * An IfThenElseBlock is the structured block representing an if
@ -90,7 +92,7 @@ public class IfThenElseBlock extends StructuredBlock {
* @return false, if oldBlock wasn't a direct sub block. * @return false, if oldBlock wasn't a direct sub block.
*/ */
public boolean replaceSubBlock(StructuredBlock oldBlock, public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) { StructuredBlock newBlock) {
if (thenBlock == oldBlock) if (thenBlock == oldBlock)
thenBlock = newBlock; thenBlock = newBlock;
else if (elseBlock == oldBlock) else if (elseBlock == oldBlock)

@ -26,7 +26,9 @@ import jode.expr.StoreInstruction;
import jode.expr.LocalStoreOperator; import jode.expr.LocalStoreOperator;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* This is the structured block for atomic instructions. * This is the structured block for atomic instructions.
@ -51,10 +53,6 @@ public class InstructionBlock extends InstructionContainer {
super(instr); super(instr);
} }
public InstructionBlock(Expression instr, Jump jump) {
super(instr, jump);
}
/** /**
* This does take the instr into account and modifies stack * This does take the instr into account and modifies stack
* accordingly. It then calls super.mapStackToLocal. * accordingly. It then calls super.mapStackToLocal.

@ -24,7 +24,9 @@ import jode.expr.InvokeOperator;
import jode.expr.LocalVarOperator; import jode.expr.LocalVarOperator;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* This is a method for block containing a single instruction. * This is a method for block containing a single instruction.
@ -39,11 +41,6 @@ public abstract class InstructionContainer extends StructuredBlock {
this.instr = instr; this.instr = instr;
} }
public InstructionContainer(Expression instr, Jump jump) {
this(instr);
setJump(jump);
}
/** /**
* Make the declarations, i.e. initialize the declare variable * Make the declarations, i.e. initialize the declare variable
* to correct values. This will declare every variable that * to correct values. This will declare every variable that

@ -34,13 +34,24 @@ public class JsrBlock extends StructuredBlock {
*/ */
StructuredBlock innerBlock; StructuredBlock innerBlock;
public JsrBlock(Jump subroutine, Jump next) { public JsrBlock() {
innerBlock = new EmptyBlock(subroutine); innerBlock = new EmptyBlock();
innerBlock.outer = this; innerBlock.outer = this;
setJump(next);
} }
/**
* Sets the successors of this structured block. This should be only
* called once, by FlowBlock.setSuccessors().
*/
public void setSuccessors(Jump[] jumps) {
if (jumps.length != 2) {
/* A conditional block can only exactly two jumps. */
throw new IllegalArgumentException("Not exactly two jumps.");
}
innerBlock.setJump(jumps[0]);
setJump(jumps[1]);
}
/* The implementation of getNext[Flow]Block is the standard /* The implementation of getNext[Flow]Block is the standard
* implementation */ * implementation */

@ -28,7 +28,9 @@ import jode.expr.LocalStoreOperator;
import jode.expr.CombineableOperator; import jode.expr.CombineableOperator;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* This is the structured block for an Loop block. * This is the structured block for an Loop block.
@ -211,6 +213,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
bodyBlock = newBlock; bodyBlock = newBlock;
else else
return false; return false;
newBlock.outer = this;
oldBlock.outer = null;
return true; return true;
} }

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -20,8 +20,10 @@
package jode.flow; package jode.flow;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collections;
import java.util.Set;
///#enddef
/** /**
* This block represents a ret instruction. A ret instruction is * This block represents a ret instruction. A ret instruction is

@ -35,7 +35,11 @@ public class ReturnBlock extends InstructionContainer {
} }
public ReturnBlock(Expression instr) { public ReturnBlock(Expression instr) {
super(instr, new Jump(FlowBlock.END_OF_METHOD)); super(instr);
}
public boolean jumpMayBeChanged() {
return true;
} }
/** /**

@ -24,7 +24,9 @@ import jode.expr.LocalStoreOperator;
import jode.expr.StoreInstruction; import jode.expr.StoreInstruction;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* A sequential block combines exactly two structured blocks to a new * A sequential block combines exactly two structured blocks to a new
@ -219,7 +221,7 @@ public class SequentialBlock extends StructuredBlock {
* @return false, if oldBlock wasn't a direct sub block. * @return false, if oldBlock wasn't a direct sub block.
*/ */
public boolean replaceSubBlock(StructuredBlock oldBlock, public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) { StructuredBlock newBlock) {
for (int i=0; i<2; i++) { for (int i=0; i<2; i++) {
if (subBlocks[i] == oldBlock) { if (subBlocks[i] == oldBlock) {
subBlocks[i] = newBlock; subBlocks[i] = newBlock;

@ -21,9 +21,12 @@ package jode.flow;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.util.ArrayEnum; import jode.util.ArrayEnum;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.AbstractSet; import java.util.Collection;
import @COLLECTIONS@.Iterator; import java.util.AbstractSet;
import java.util.Set;
import java.util.Iterator;
///#enddef
/** /**
* This class represents a set of local info, all having different * This class represents a set of local info, all having different
@ -177,7 +180,7 @@ public final class SlotSet extends AbstractSet implements Cloneable {
LocalInfo li = locals[i]; LocalInfo li = locals[i];
int slot = li.getSlot(); int slot = li.getSlot();
for (int j=0; j<vs.count; j++) { for (int j=0; j<vs.count; j++) {
if (li.getSlot() == vs.locals[j].getSlot()) if (slot == vs.locals[j].getSlot())
li.combineWith(vs.locals[j]); li.combineWith(vs.locals[j]);
} }
} }
@ -190,7 +193,19 @@ public final class SlotSet extends AbstractSet implements Cloneable {
* *
* @param kill The other kill set. * @param kill The other kill set.
*/ */
public void mergeKill(SlotSet kill) { public void mergeKill(LocalInfo li) {
if (!containsSlot(li.getSlot()))
add(li.getLocalInfo());
}
/**
* Add the slots in kill to the current set, unless there are
* already in this set. This differs from addAll, in the fact that it
* doesn't combine the locals.
*
* @param kill The other kill set.
*/
public void mergeKill(Set kill) {
grow(kill.size()); grow(kill.size());
big_loop: big_loop:
for (Iterator i = kill.iterator(); i.hasNext(); ) { for (Iterator i = kill.iterator(); i.hasNext(); ) {

@ -46,11 +46,10 @@ public class SpecialBlock extends StructuredBlock {
*/ */
int depth; int depth;
public SpecialBlock(int type, int count, int depth, Jump jump) { public SpecialBlock(int type, int count, int depth) {
this.type = type; this.type = type;
this.count = count; this.count = count;
this.depth = depth; this.depth = depth;
setJump(jump);
} }
/** /**

@ -26,9 +26,11 @@ import jode.decompiler.Declarable;
import jode.decompiler.ClassAnalyzer; import jode.decompiler.ClassAnalyzer;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collections;
import @COLLECTIONS@.Set; import java.util.Iterator;
import java.util.Set;
///#enddef
/** /**
* A structured block is the building block of the source programm. * A structured block is the building block of the source programm.
@ -118,6 +120,18 @@ public abstract class StructuredBlock {
return null; return null;
} }
/**
* Sets the successors of this structured block. This should be only
* called once, by FlowBlock.setSuccessors().
*/
public void setSuccessors(Jump[] jumps) {
if (jumps.length > 1) {
/* A normal block can only handle a single jump. */
throw new IllegalArgumentException("Too many jumps.");
}
setJump(jumps[0]);
}
public void setJump(Jump jump) { public void setJump(Jump jump) {
this.jump = jump; this.jump = jump;
jump.prev = this; jump.prev = this;
@ -199,7 +213,8 @@ public abstract class StructuredBlock {
/** /**
* Removes the jump. This does not update the successors vector * Removes the jump. This does not update the successors vector
* of the flow block, you have to do it yourself. */ * of the flow block, you have to do it yourself.
*/
public final void removeJump() { public final void removeJump() {
if (jump != null) { if (jump != null) {
jump.prev = null; jump.prev = null;
@ -560,21 +575,9 @@ public abstract class StructuredBlock {
* @param in The VariableSet, the in variables should be stored to. * @param in The VariableSet, the in variables should be stored to.
*/ */
public void fillInGenSet(Set in, Set gen) { public void fillInGenSet(Set in, Set gen) {
/* overwritten by InstructionContainer */
}
/**
* Add all the successors of this block and all subblocks to the
* flow block.
* @param succs The vector, the successors should be stored to.
*/
public void fillSuccessors() {
if (jump != null)
flowBlock.addSuccessor(jump);
StructuredBlock[] subs = getSubBlocks(); StructuredBlock[] subs = getSubBlocks();
for (int i=0; i<subs.length; i++) { for (int i=0; i<subs.length; i++)
subs[i].fillSuccessors(); subs[i].fillInGenSet(in, gen);
}
} }
/** /**

@ -21,6 +21,11 @@ package jode.flow;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import jode.expr.Expression; import jode.expr.Expression;
///#def COLLECTIONS java.util
import java.util.Arrays;
import java.util.Comparator;
///#enddef
/** /**
* This is the structured block for an empty block. * This is the structured block for an empty block.
*/ */
@ -30,55 +35,81 @@ implements BreakableBlock {
VariableStack exprStack; VariableStack exprStack;
VariableStack breakedStack; VariableStack breakedStack;
public SwitchBlock(Expression instr, public SwitchBlock(Expression instr, int[] cases) {
int[] cases, FlowBlock[] dests) {
super(instr); super(instr);
this.caseBlocks = new CaseBlock[cases.length + 1];
/* First remove all dests that jump to the default dest. */
int numCases = dests.length;
FlowBlock defaultDest = dests[cases.length];
for (int i=0; i< cases.length; i++) { for (int i=0; i< cases.length; i++) {
if (dests[i] == defaultDest) { caseBlocks[i] = new CaseBlock(cases[i]);
dests[i] = null; caseBlocks[i].outer = this;
numCases--; }
} caseBlocks[cases.length] = new CaseBlock(true);
caseBlocks[cases.length].outer = this;
isBreaked = false;
}
/**
* Sets the successors of this structured block. This should be only
* called once, by FlowBlock.setSuccessors().
*/
public void setSuccessors(Jump[] jumps) {
if (jumps.length != caseBlocks.length) {
/* A conditional block can only exactly two jumps. */
throw new IllegalArgumentException("Wrong number of jumps.");
} }
for (int i=0; i < caseBlocks.length; i++)
caseBlocks[i].subBlock.setJump(jumps[i]);
doJumpTrafo();
}
caseBlocks = new CaseBlock[numCases]; public boolean doTransformations() {
FlowBlock lastDest = null; return super.doTransformations();
for (int i=numCases-1; i>=0; i--) { }
/**
* Sort the destinations by finding the greatest destAddr public void doJumpTrafo() {
*/ /* First remember the default destination */
int index = 0; FlowBlock defaultDest
for (int j=1; j<dests.length; j++) { = caseBlocks[caseBlocks.length-1].subBlock.jump.destination;
if (dests[j] != null Comparator caseBlockComparator = new Comparator() {
&& (dests[index] == null public int compare(Object o1, Object o2) {
|| dests[j].getAddr() >= dests[index].getAddr())) CaseBlock c1 = (CaseBlock) o1;
index = j; CaseBlock c2 = (CaseBlock) o2;
int d1 = c1.subBlock.jump.destination.getBlockNr();
int d2 = c2.subBlock.jump.destination.getBlockNr();
if (d1 != d2)
return d1 - d2;
if (c2.isDefault)
return -1;
if (c1.isDefault)
return 1;
if (c1.value < c2.value)
return -1;
if (c1.value > c2.value)
return 1;
return 0;
} }
/* assert(dests[index] != null) */ };
Arrays.sort(caseBlocks, caseBlockComparator);
int value;
if (index == cases.length) int newCases = 0;
value = -1; for (int i=0; i < caseBlocks.length; i++) {
else Jump jump = caseBlocks[i].subBlock.jump;
value = cases[index]; if (i < caseBlocks.length - 1
&& jump.destination
if (dests[index] == lastDest) == caseBlocks[i+1].subBlock.jump.destination) {
caseBlocks[i] = new CaseBlock(value); // This case falls into the next one.
else caseBlocks[i].subBlock.removeJump();
caseBlocks[i] = new CaseBlock(value, flowBlock.removeSuccessor(jump);
new Jump(dests[index]));
caseBlocks[i].outer = this; if (caseBlocks[i+1].subBlock.jump.destination == defaultDest)
lastDest = dests[index]; continue; // remove this case, it jumps to the default.
dests[index] = null; }
if (index == cases.length) caseBlocks[newCases++] = caseBlocks[i];
caseBlocks[i].isDefault = true; }
} caseBlocks[newCases-1].isLastBlock = true;
caseBlocks[numCases-1].isLastBlock = true;
this.jump = null; CaseBlock[] newCaseBlocks = new CaseBlock[newCases];
isBreaked = false; System.arraycopy(caseBlocks, 0, newCaseBlocks, 0, newCases);
caseBlocks = newCaseBlocks;
} }
/** /**
@ -195,7 +226,7 @@ implements BreakableBlock {
writer.tab(); writer.tab();
} }
writer.print("switch ("); writer.print("switch (");
instr.dumpExpression(writer.EXPL_PAREN, writer); instr.dumpExpression(writer);
writer.print(")"); writer.print(")");
writer.openBrace(); writer.openBrace();
for (int i=0; i < caseBlocks.length; i++) for (int i=0; i < caseBlocks.length; i++)

@ -23,7 +23,9 @@ import jode.decompiler.TabbedPrintWriter;
import jode.expr.Expression; import jode.expr.Expression;
import jode.util.SimpleSet; import jode.util.SimpleSet;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/** /**
* This class represents a synchronized structured block. * This class represents a synchronized structured block.
@ -106,6 +108,15 @@ public class SynchronizedBlock extends StructuredBlock {
super.simplify(); super.simplify();
} }
/**
* Determines if there is a sub block, that flows through to the end
* of this block. If this returns true, you know that jump is null.
* @return true, if the jump may be safely changed.
*/
public boolean jumpMayBeChanged() {
return (bodyBlock.jump != null || bodyBlock.jumpMayBeChanged());
}
public boolean doTransformations() { public boolean doTransformations() {
StructuredBlock last = flowBlock.lastModified; StructuredBlock last = flowBlock.lastModified;

@ -32,8 +32,8 @@ import jode.expr.*;
import jode.type.MethodType; import jode.type.MethodType;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
import java.io.IOException;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;
@ -613,7 +613,8 @@ public class TransformConstructors {
|| isStatic || type01Count == 0) || isStatic || type01Count == 0)
return; return;
checkAnonymousConstructor(); if ((Options.options & Options.OPTION_ANON) != 0)
checkAnonymousConstructor();
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0) & GlobalOptions.DEBUG_CONSTRS) != 0)
@ -854,32 +855,37 @@ public class TransformConstructors {
InvokeOperator superInvoke = (InvokeOperator) InvokeOperator superInvoke = (InvokeOperator)
ib.getInstruction().simplify(); ib.getInstruction().simplify();
ClassInfo superClazz = superInvoke.getClassInfo(); ClassInfo superClazz = superInvoke.getClassInfo();
InnerClassInfo[] outers = superClazz.getOuterClasses();
int superParamCount = superInvoke.getSubExpressions().length - 1; int superParamCount = superInvoke.getSubExpressions().length - 1;
if ((Options.options & Options.OPTION_INNER) != 0 if (superClazz != null) {
&& outers != null try {
&& outers[0].outer != null superClazz.load(ClassInfo.OUTERCLASS);
&& outers[0].name != null if ((Options.options & Options.OPTION_INNER) != 0
&& !Modifier.isStatic(outers[0].modifiers)) { && superClazz != null
&& superClazz.getOuterClass() != null
if (superParamCount != 1 && !Modifier.isStatic(superClazz.getModifiers())) {
|| !(superInvoke.getSubExpressions()[1]
instanceof ThisOperator)) if (superParamCount != 1
continue; || !(superInvoke.getSubExpressions()[1]
} else { instanceof ThisOperator))
/* If the super() has no parameters (or only default continue;
* outerValue parameter for inner/anonymous classes), we } else {
* can remove it /* If the super() has no parameters (or only
*/ * default outerValue parameter for
ClassAnalyzer superClazzAna = superInvoke.getClassAnalyzer(); * inner/anonymous classes), we can remove it */
OuterValues superOV = null; ClassAnalyzer superClazzAna
if (superClazzAna != null) = superInvoke.getClassAnalyzer();
superOV = superClazzAna.getOuterValues(); OuterValues superOV = null;
if (superParamCount > 0 if (superClazzAna != null)
&& (superOV == null superOV = superClazzAna.getOuterValues();
|| superParamCount > superOV.getCount())) if (superParamCount > 0
continue; && (superOV == null
|| superParamCount > superOV.getCount()))
continue;
}
} catch (IOException ex) {
/* Ignore */
}
} }
ib.removeBlock(); ib.removeBlock();
if (i > type0Count) { if (i > type0Count) {

@ -24,12 +24,16 @@ import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.expr.*; import jode.expr.*;
import @COLLECTIONS@.TreeSet; ///#def COLLECTIONS java.util
import @COLLECTIONS@.SortedSet; import java.util.TreeSet;
import @COLLECTIONS@.Set; import java.util.SortedSet;
import @COLLECTIONS@.Map; import java.util.Set;
import @COLLECTIONS@.Iterator; import java.util.Map;
import @COLLECTIONEXTRA@.Comparable; import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.Comparable;
///#enddef
/** /**
* *
@ -37,17 +41,18 @@ import @COLLECTIONEXTRA@.Comparable;
*/ */
public class TransformExceptionHandlers { public class TransformExceptionHandlers {
SortedSet handlers; SortedSet handlers;
FlowBlock[] flowBlocks;
static class Handler implements Comparable { static class Handler implements Comparable {
FlowBlock start; FlowBlock start;
int endAddr; FlowBlock end;
FlowBlock handler; FlowBlock handler;
Type type; Type type;
public Handler(FlowBlock tryBlock, int end, public Handler(FlowBlock tryBlock, FlowBlock endBlock,
FlowBlock catchBlock, Type type) { FlowBlock catchBlock, Type type) {
this.start = tryBlock; this.start = tryBlock;
this.endAddr = end; this.end = endBlock;
this.handler = catchBlock; this.handler = catchBlock;
this.type = type; this.type = type;
} }
@ -55,19 +60,19 @@ public class TransformExceptionHandlers {
public int compareTo (Object o) { public int compareTo (Object o) {
Handler second = (Handler) o; Handler second = (Handler) o;
/* First sort by start offsets, highest address first...*/ /* First sort by start offsets, highest block number first...*/
if (start.getAddr() != second.start.getAddr()) if (start.getBlockNr() != second.start.getBlockNr())
/* this subtraction is save since addresses are only 16 bit */ /* this subtraction is save since block numbers are only 16 bit */
return second.start.getAddr() - start.getAddr(); return second.start.getBlockNr() - start.getBlockNr();
/* ...Second sort by end offsets, lowest address first... /* ...Second sort by end offsets, lowest block number first...
* this will move the innermost blocks to the beginning. */ * this will move the innermost blocks to the beginning. */
if (endAddr != second.endAddr) if (end.getBlockNr() != second.end.getBlockNr())
return endAddr - second.endAddr; return end.getBlockNr() - second.end.getBlockNr();
/* ...Last sort by handler offsets, lowest first */ /* ...Last sort by handler offsets, lowest first */
if (handler.getAddr() != second.handler.getAddr()) if (handler.getBlockNr() != second.handler.getBlockNr())
return handler.getAddr() - second.handler.getAddr(); return handler.getBlockNr() - second.handler.getBlockNr();
/* ...Last sort by typecode signature. Shouldn't happen to often. /* ...Last sort by typecode signature. Shouldn't happen to often.
*/ */
@ -82,20 +87,21 @@ public class TransformExceptionHandlers {
} }
} }
public TransformExceptionHandlers() { public TransformExceptionHandlers(FlowBlock[] flowBlocks) {
handlers = new TreeSet(); handlers = new TreeSet();
this.flowBlocks = flowBlocks;
} }
/** /**
* Add an exception Handler. * Add an exception Handler.
* @param start The start address of the exception range. * @param start The start block number of the exception range.
* @param end The end address of the exception range + 1. * @param end The end block number of the exception range + 1.
* @param handler The address of the handler. * @param handler The block number of the handler.
* @param type The type of the exception, null for ALL. * @param type The type of the exception, null for ALL.
*/ */
public void addHandler(FlowBlock tryBlock, int end, public void addHandler(FlowBlock tryBlock, FlowBlock endBlock,
FlowBlock catchBlock, Type type) { FlowBlock catchBlock, Type type) {
handlers.add(new Handler(tryBlock, end, catchBlock, type)); handlers.add(new Handler(tryBlock, endBlock, catchBlock, type));
} }
@ -118,7 +124,7 @@ public class TransformExceptionHandlers {
((TryBlock)tryFlow.block).addCatchBlock(newBlock); ((TryBlock)tryFlow.block).addCatchBlock(newBlock);
newBlock.setCatchBlock(catchFlow.block); newBlock.setCatchBlock(catchFlow.block);
tryFlow.mergeSuccessors(catchFlow); tryFlow.mergeSuccessors(catchFlow);
tryFlow.mergeAddr(catchFlow); tryFlow.mergeBlockNr(catchFlow);
} }
/* And now the complicated parts. */ /* And now the complicated parts. */
@ -260,12 +266,6 @@ public class TransformExceptionHandlers {
jumps != null; jumps = jumps.next, isFirstJump = false) { jumps != null; jumps = jumps.next, isFirstJump = false) {
StructuredBlock prev = jumps.prev; StructuredBlock prev = jumps.prev;
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch-all block
* that will do the finally.
*/
continue;
}
if (prev instanceof JsrBlock) { if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr. /* The jump is directly preceeded by a jsr.
* Everything okay. * Everything okay.
@ -306,8 +306,8 @@ public class TransformExceptionHandlers {
* lies outside the try/catch block. * lies outside the try/catch block.
*/ */
if (jumps.destination.predecessors.size() == 1 if (jumps.destination.predecessors.size() == 1
&& jumps.destination.getAddr() >= startOutExit && jumps.destination.getBlockNr() >= startOutExit
&& jumps.destination.getNextAddr() <= endOutExit) { && jumps.destination.getNextBlockNr() <= endOutExit) {
jumps.destination.analyze(startOutExit, endOutExit); jumps.destination.analyze(startOutExit, endOutExit);
StructuredBlock sb = jumps.destination.block; StructuredBlock sb = jumps.destination.block;
@ -380,12 +380,6 @@ public class TransformExceptionHandlers {
StructuredBlock prev = jumps.prev; StructuredBlock prev = jumps.prev;
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch all block
* that will do the monitorexit.
*/
continue;
}
if (prev instanceof JsrBlock) { if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr. /* The jump is directly preceeded by a jsr.
*/ */
@ -448,8 +442,8 @@ public class TransformExceptionHandlers {
*/ */
if (prev instanceof JsrBlock) { if (prev instanceof JsrBlock) {
if (subRoutine == null if (subRoutine == null
&& successor.getAddr() >= startMonExit && successor.getBlockNr() >= startMonExit
&& successor.getNextAddr() <= endMonExit) { && successor.getNextBlockNr() <= endMonExit) {
successor.analyze(startMonExit, endMonExit); successor.analyze(startMonExit, endMonExit);
if (isMonitorExitSubRoutine(successor, local)) if (isMonitorExitSubRoutine(successor, local))
@ -467,8 +461,8 @@ public class TransformExceptionHandlers {
* block. * block.
*/ */
if (successor.predecessors.size() == 1 if (successor.predecessors.size() == 1
&& successor.getAddr() >= startOutExit && successor.getBlockNr() >= startOutExit
&& successor.getNextAddr() <= endOutExit) { && successor.getNextBlockNr() <= endOutExit) {
successor.analyze(startOutExit, endOutExit); successor.analyze(startOutExit, endOutExit);
StructuredBlock sb = successor.block; StructuredBlock sb = successor.block;
@ -479,8 +473,8 @@ public class TransformExceptionHandlers {
StructuredBlock jsrInner = sb.getSubBlocks()[0]; StructuredBlock jsrInner = sb.getSubBlocks()[0];
FlowBlock dest = jsrInner.jump.destination; FlowBlock dest = jsrInner.jump.destination;
if (subRoutine == null if (subRoutine == null
&& dest.getAddr() >= startMonExit && dest.getBlockNr() >= startMonExit
&& dest.getNextAddr() <= endMonExit) { && dest.getNextBlockNr() <= endMonExit) {
dest.analyze(startMonExit, endMonExit); dest.analyze(startMonExit, endMonExit);
if (isMonitorExitSubRoutine(dest, local)) if (isMonitorExitSubRoutine(dest, local))
subRoutine = dest; subRoutine = dest;
@ -513,7 +507,7 @@ public class TransformExceptionHandlers {
if (subRoutine != null) { if (subRoutine != null) {
removeJSR(tryFlow, subRoutine); removeJSR(tryFlow, subRoutine);
tryFlow.mergeAddr(subRoutine); tryFlow.mergeBlockNr(subRoutine);
} }
} }
@ -559,11 +553,6 @@ public class TransformExceptionHandlers {
* return_n * return_n
*/ */
/* Now remove the jump (after the throw) from the
* catch block so that we can forget about it.
*/
catchFlow.removeSuccessor(catchBlock.subBlocks[1].jump);
MonitorExitOperator monexit = (MonitorExitOperator) MonitorExitOperator monexit = (MonitorExitOperator)
((InstructionBlock) catchBlock.subBlocks[0]).instr; ((InstructionBlock) catchBlock.subBlocks[0]).instr;
LocalInfo local = LocalInfo local =
@ -573,16 +562,16 @@ public class TransformExceptionHandlers {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeSynchronized(" + tryFlow.getAddr() ("analyzeSynchronized(" + tryFlow.getBlockNr()
+ "," + tryFlow.getNextAddr() + "," + catchFlow.getAddr() + "," + tryFlow.getNextBlockNr() + "," + catchFlow.getBlockNr()
+ "," + catchFlow.getNextAddr() + "," + endHandler + ")"); + "," + catchFlow.getNextBlockNr() + "," + endHandler + ")");
checkAndRemoveMonitorExit checkAndRemoveMonitorExit
(tryFlow, local, (tryFlow, local,
tryFlow.getNextAddr(), catchFlow.getAddr(), tryFlow.getNextBlockNr(), catchFlow.getBlockNr(),
catchFlow.getNextAddr(), endHandler); catchFlow.getNextBlockNr(), endHandler);
tryFlow.mergeAddr(catchFlow); tryFlow.mergeBlockNr(catchFlow);
SynchronizedBlock syncBlock = new SynchronizedBlock(local); SynchronizedBlock syncBlock = new SynchronizedBlock(local);
TryBlock tryBlock = (TryBlock) tryFlow.block; TryBlock tryBlock = (TryBlock) tryFlow.block;
syncBlock.replace(tryBlock); syncBlock.replace(tryBlock);
@ -686,25 +675,10 @@ public class TransformExceptionHandlers {
if (!(jsrBlock.innerBlock instanceof BreakBlock)) if (!(jsrBlock.innerBlock instanceof BreakBlock))
return false; return false;
/* Check if the try block has no exit (except throws) /* Check if the try block has no exit
*/
Set succs = tryFlow.getSuccessors();
if (succs.size() > 0) {
if (!succs.contains(FlowBlock.END_OF_METHOD))
return false;
Jump throwJumps
= tryFlow.getJumps(FlowBlock.END_OF_METHOD);
for (/**/; throwJumps != null;
throwJumps = throwJumps.next) {
if (!(throwJumps.prev instanceof ThrowBlock))
/* There is a return exit in the try block */
return false;
}
}
/* Remove the jump of the throw instruction.
*/ */
catchFlow.removeSuccessor(throwBlock.jump); if (tryFlow.getSuccessors().size() > 0)
throwBlock.removeJump(); return false;
/* Replace the catchBlock with the finallyBlock. /* Replace the catchBlock with the finallyBlock.
*/ */
@ -712,46 +686,41 @@ public class TransformExceptionHandlers {
transformSubRoutine(finallyBlock); transformSubRoutine(finallyBlock);
tryFlow.updateInOutCatch(catchFlow); tryFlow.updateInOutCatch(catchFlow);
tryFlow.mergeAddr(catchFlow); tryFlow.mergeBlockNr(catchFlow);
finallyBlock = catchFlow.block; finallyBlock = catchFlow.block;
tryFlow.mergeSuccessors(catchFlow); tryFlow.mergeSuccessors(catchFlow);
} else { } else {
FlowBlock subRoutine = jsrBlock.innerBlock.jump.destination; FlowBlock subRoutine = jsrBlock.innerBlock.jump.destination;
checkAndRemoveJSR(tryFlow, subRoutine, checkAndRemoveJSR(tryFlow, subRoutine,
tryFlow.getNextAddr(), catchFlow.getAddr()); tryFlow.getNextBlockNr(), catchFlow.getBlockNr());
while (subRoutine.analyze(catchFlow.getNextAddr(), end)); while (subRoutine.analyze(catchFlow.getNextBlockNr(), end));
/* Now check if the subroutine is correct and has only the /* Now check if the subroutine is correct and has only the
* catchFlow as predecessor. * catchFlow as predecessor.
*/ */
if (subRoutine.predecessors.size() == 1 if (subRoutine.predecessors.size() == 1
&& transformSubRoutine(subRoutine.block)) { && transformSubRoutine(subRoutine.block)) {
subRoutine.mergeAddr(catchFlow); subRoutine.mergeBlockNr(catchFlow);
/* Now remove the jump to the JSR from the catch block /* Now remove the jump to the JSR from the catch block
* and the jump of the throw instruction.
*/ */
catchFlow.removeSuccessor(jsrBlock.innerBlock.jump); catchFlow.removeSuccessor(jsrBlock.innerBlock.jump);
jsrBlock.innerBlock.removeJump(); jsrBlock.innerBlock.removeJump();
catchFlow.removeSuccessor(throwBlock.jump);
throwBlock.removeJump();
tryFlow.updateInOutCatch(subRoutine); tryFlow.updateInOutCatch(subRoutine);
tryFlow.mergeAddr(subRoutine); tryFlow.mergeBlockNr(subRoutine);
tryFlow.mergeSuccessors(subRoutine); tryFlow.mergeSuccessors(subRoutine);
finallyBlock = subRoutine.block; finallyBlock = subRoutine.block;
} else { } else {
catchFlow.removeSuccessor(throwBlock.jump);
throwBlock.removeJump();
finallyBlock = jsrBlock; finallyBlock = jsrBlock;
finallyBlock.replace(catchFlow.block); finallyBlock.replace(catchFlow.block);
transformSubRoutine(finallyBlock); transformSubRoutine(finallyBlock);
tryFlow.updateInOutCatch(catchFlow); tryFlow.updateInOutCatch(catchFlow);
tryFlow.mergeAddr(catchFlow); tryFlow.mergeBlockNr(catchFlow);
finallyBlock = catchFlow.block; finallyBlock = catchFlow.block;
tryFlow.mergeSuccessors(catchFlow); tryFlow.mergeSuccessors(catchFlow);
} }
@ -797,22 +766,16 @@ public class TransformExceptionHandlers {
FlowBlock dest = (FlowBlock) iter.next(); FlowBlock dest = (FlowBlock) iter.next();
if (dest == succ) if (dest == succ)
continue; continue;
if (dest != FlowBlock.END_OF_METHOD) { /* There is another exit in the try block, bad */
/* There is another exit in the try block, bad */ return false;
return false;
}
for (Jump throwJumps = (Jump) tryFlow.getJumps(dest);
throwJumps != null; throwJumps = throwJumps.next) {
if (!(throwJumps.prev instanceof ThrowBlock)) {
/* There is a return exit in the try block */
return false;
}
}
} }
/* remove the pop now */ /* remove the pop now */
firstInstr.removeBlock(); EmptyBlock eb = new EmptyBlock();
tryFlow.mergeAddr(catchFlow); eb.moveJump(firstInstr.jump);
eb.replace(firstInstr);
if (catchFlow.lastModified == firstInstr)
catchFlow.lastModified = eb;
if (succ != null) { if (succ != null) {
Jump jumps = tryFlow.removeJumps(succ); Jump jumps = tryFlow.removeJumps(succ);
@ -836,15 +799,16 @@ public class TransformExceptionHandlers {
/* try block has no successors */ /* try block has no successors */
if (succ != null && succ.predecessors.size() == 1) { if (succ != null && succ.predecessors.size() == 1) {
while (succ.analyze(catchFlow.getNextAddr(), end)); catchFlow.checkConsistent();
tryFlow.mergeAddr(succ); while (catchFlow.analyze(catchFlow.getBlockNr(), end));
tryFlow.removeJumps(succ); newBlock.setCatchBlock(catchFlow.block);
newBlock.setCatchBlock(succ.block); tryFlow.mergeBlockNr(catchFlow);
tryFlow.mergeSuccessors(succ); tryFlow.mergeSuccessors(catchFlow);
} else { } else {
/* Put the catchBlock in instead. /* Put the catchBlock in instead.
*/ */
newBlock.setCatchBlock(catchFlow.block); newBlock.setCatchBlock(catchFlow.block);
tryFlow.mergeBlockNr(catchFlow);
tryFlow.mergeSuccessors(catchFlow); tryFlow.mergeSuccessors(catchFlow);
} }
return true; return true;
@ -865,23 +829,27 @@ public class TransformExceptionHandlers {
Handler last = null; Handler last = null;
for (Iterator i = handlers.iterator(); i.hasNext(); ) { for (Iterator i = handlers.iterator(); i.hasNext(); ) {
Handler exc = (Handler) i.next(); Handler exc = (Handler) i.next();
int start = exc.start.getAddr(); int start = exc.start.getBlockNr();
int end = exc.endAddr; int end = exc.end.getBlockNr();
int handler = exc.handler.getAddr(); int handler = exc.handler.getBlockNr();
if (start >= end || handler < end) if (start > end || handler <= end)
throw new AssertError throw new AssertError
("ExceptionHandler order failed: not " ("ExceptionHandler order failed: not "
+ start + " < " + end + " <= " + handler); + start + " < " + end + " <= " + handler);
if (last != null if (last != null
&& (last.start.getAddr() != start || last.endAddr != end)) { && (last.start.getBlockNr() != start
|| last.end.getBlockNr() != end)) {
/* The last handler does catch another range. /* The last handler does catch another range.
* Due to the order: * Due to the order:
* start < last.start.getAddr() || end > last.end.getAddr() * start < last.start.getBlockNr()
* || end > last.end.getBlockNr()
*/ */
if (end > last.start.getAddr() && end < last.endAddr) if (end >= last.start.getBlockNr()
&& end < last.end.getBlockNr())
throw new AssertError throw new AssertError
("Exception handlers ranges are intersecting: [" ("Exception handlers ranges are intersecting: ["
+ last.start.getAddr()+", "+last.endAddr+"] and [" + last.start.getBlockNr()+", "
+ last.end.getBlockNr()+"] and ["
+ start+", "+end+"]."); + start+", "+end+"].");
} }
last = exc; last = exc;
@ -895,30 +863,41 @@ public class TransformExceptionHandlers {
while(next != null) { while(next != null) {
Handler last = exc; Handler last = exc;
exc = next; exc = next;
int startNr = exc.start.getBlockNr();
int endNr = exc.end.getBlockNr();
next = i.hasNext() ? (Handler) i.next() : null; next = i.hasNext() ? (Handler) i.next() : null;
int endHandler = Integer.MAX_VALUE; int endHandler = Integer.MAX_VALUE;
/* If the next exception handler catches a bigger range /* If the next exception handler catches a bigger range
* it must surround the handler completely. * it must surround the handler completely.
*/ */
if (next != null && next.endAddr > exc.endAddr) if (next != null
endHandler = next.endAddr; && next.end.getBlockNr() > endNr)
endHandler = next.end.getBlockNr() + 1;
FlowBlock tryFlow = exc.start; FlowBlock tryFlow = exc.start;
tryFlow.checkConsistent(); tryFlow.checkConsistent();
if (last == null || exc.type == null if (last == null || exc.type == null
|| last.start.getAddr() != exc.start.getAddr() || last.start.getBlockNr() != startNr
|| last.endAddr != exc.endAddr) { || last.end.getBlockNr() != endNr) {
/* The last handler does catch another range. /* The last handler does catch another range.
* Create a new try block. * Create a new try block.
*/ */
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeTry(" ("analyzeTry(" + startNr + ", " + endNr+")");
+ exc.start.getAddr() + ", " + exc.endAddr+")"); while(true) {
while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr)); while (tryFlow.analyze(startNr,
endNr+1));
int nextNr = tryFlow.getNextBlockNr();
if (nextNr > endNr)
break;
tryFlow = flowBlocks[nextNr];
}
if (tryFlow.getBlockNr() != startNr)
GlobalOptions.err.println
("Warning: Can't completely analyze try.");
TryBlock tryBlock = new TryBlock(tryFlow); TryBlock tryBlock = new TryBlock(tryFlow);
} else if (!(tryFlow.block instanceof TryBlock)) } else if (!(tryFlow.block instanceof TryBlock))
throw new AssertError("no TryBlock"); throw new AssertError("no TryBlock");
@ -941,22 +920,21 @@ public class TransformExceptionHandlers {
* create a new flow block, that jumps to the handler. * create a new flow block, that jumps to the handler.
* This will be our new exception handler. * This will be our new exception handler.
*/ */
EmptyBlock jump = new EmptyBlock(new Jump(catchFlow)); FlowBlock newFlow = new FlowBlock
FlowBlock newFlow = new FlowBlock(catchFlow.method, (catchFlow.method, catchFlow.getBlockNr(),
catchFlow.getAddr()); catchFlow.prevByCodeOrder);
newFlow.appendBlock(jump, 0); newFlow.setSuccessors(new FlowBlock[] { catchFlow });
catchFlow.prevByAddr.nextByAddr = newFlow; newFlow.nextByCodeOrder = catchFlow;
newFlow.prevByAddr = catchFlow.prevByAddr; catchFlow.prevByCodeOrder = newFlow;
newFlow.nextByAddr = catchFlow;
catchFlow.prevByAddr = newFlow;
catchFlow = newFlow; catchFlow = newFlow;
} else { } else {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeCatch(" ("analyzeCatch("
+ catchFlow.getAddr() + ", " + endHandler + ")"); + catchFlow.getBlockNr() + ", " + endHandler + ")");
while (catchFlow.analyze(catchFlow.getAddr(), endHandler)); while (catchFlow.analyze(catchFlow.getBlockNr(),
endHandler));
} }
tryFlow.updateInOutCatch(catchFlow); tryFlow.updateInOutCatch(catchFlow);
@ -974,8 +952,8 @@ public class TransformExceptionHandlers {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeTryCatch(" + tryFlow.getAddr() + ", " ("analyzeTryCatch(" + tryFlow.getBlockNr() + ", "
+ tryFlow.getNextAddr() + ") done."); + tryFlow.getNextBlockNr() + ") done.");
} }
} }
} }

@ -51,7 +51,7 @@ public class TryBlock extends StructuredBlock {
StructuredBlock[] subBlocks = new StructuredBlock[1]; StructuredBlock[] subBlocks = new StructuredBlock[1];
public TryBlock(FlowBlock tryFlow) { public TryBlock(FlowBlock tryFlow) {
this.gen = (VariableSet) tryFlow.gen.clone(); this.gen = (VariableSet) tryFlow.used.clone();
this.flowBlock = tryFlow; this.flowBlock = tryFlow;
StructuredBlock bodyBlock = tryFlow.block; StructuredBlock bodyBlock = tryFlow.block;

@ -21,9 +21,11 @@ package jode.flow;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.util.ArrayEnum; import jode.util.ArrayEnum;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.AbstractSet; import java.util.Collection;
import @COLLECTIONS@.Iterator; import java.util.AbstractSet;
import java.util.Iterator;
///#enddef
/** /**
* This class represents a set of Variables, which are mainly used in * This class represents a set of Variables, which are mainly used in
@ -217,5 +219,39 @@ public final class VariableSet extends AbstractSet implements Cloneable {
add(li2.getLocalInfo()); add(li2.getLocalInfo());
} }
} }
/**
* Merges the localinfo with this gen set and remove all variables
* that are killed by it. This differs by mergeWrite in that it merges
* li.
* @param li the variable to add.
*/
public void mergeRead(LocalInfo li) {
int slot = li.getSlot();
for (int i=0; i < count; ) {
if (locals[i].getSlot() == slot) {
li.combineWith(locals[i]);
locals[i] = locals[--count];
} else
i++;
}
add(li);
}
/**
* Add li to this gen set and remove all variables that are killed
* by it.
* @param li the variable to add.
*/
public void mergeWrite(LocalInfo li) {
int slot = li.getSlot();
for (int i=0; i < count; ) {
if (locals[i].getSlot() == slot)
locals[i] = locals[--count];
else
i++;
}
add(li);
}
} }

@ -19,7 +19,7 @@
package jode.obfuscator; package jode.obfuscator;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.SearchPath; import jode.bytecode.ClassPath;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.obfuscator.modules.WildCard; import jode.obfuscator.modules.WildCard;
@ -28,17 +28,23 @@ import jode.obfuscator.modules.SimpleAnalyzer;
import java.io.*; import java.io.*;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collection;
import @COLLECTIONS@.Set; import java.util.Iterator;
import @COLLECTIONS@.HashSet; import java.util.Set;
import @COLLECTIONS@.Map; import java.util.HashSet;
import @COLLECTIONS@.HashMap; import java.util.Map;
import @COLLECTIONS@.TreeMap; import java.util.HashMap;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.TreeMap;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
///#ifdef JDK12 ///#ifdef JDK12
///import @COLLECTIONS@.WeakHashMap; ///#def COLLECTIONS java.util
import java.util.WeakHashMap;
///#enddef
///#endif ///#endif
public class ClassBundle implements OptionHandler { public class ClassBundle implements OptionHandler {
@ -49,7 +55,7 @@ public class ClassBundle implements OptionHandler {
*/ */
Set toAnalyze = new HashSet(); Set toAnalyze = new HashSet();
String classPath; ClassPath classPath;
String destDir; String destDir;
String tableFile; String tableFile;
@ -65,8 +71,6 @@ public class ClassBundle implements OptionHandler {
public ClassBundle() { public ClassBundle() {
classPath = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, SearchPath.altPathSeparatorChar);
destDir = "."; destDir = ".";
basePackage = new PackageIdentifier(this, null, "", ""); basePackage = new PackageIdentifier(this, null, "", "");
basePackage.setReachable(); basePackage.setReachable();
@ -74,9 +78,9 @@ public class ClassBundle implements OptionHandler {
} }
///#ifdef JDK12 ///#ifdef JDK12
/// private static final Map aliasesHash = new WeakHashMap(); private static final Map aliasesHash = new WeakHashMap();
///#else ///#else
private static final Map aliasesHash = new HashMap(); /// private static final Map aliasesHash = new HashMap();
///#endif ///#endif
private static final Map clazzCache = new HashMap(); private static final Map clazzCache = new HashMap();
private static final Map referenceCache = new HashMap(); private static final Map referenceCache = new HashMap();
@ -88,10 +92,10 @@ public class ClassBundle implements OptionHandler {
Iterator i = values.iterator(); Iterator i = values.iterator();
StringBuffer sb = new StringBuffer((String) i.next()); StringBuffer sb = new StringBuffer((String) i.next());
while (i.hasNext()) { while (i.hasNext()) {
sb.append(SearchPath.altPathSeparatorChar) sb.append(ClassPath.altPathSeparatorChar)
.append((String)i.next()); .append((String)i.next());
} }
ClassInfo.setClassPath(sb.toString()); classPath = new ClassPath(sb.toString());
return; return;
} }
@ -270,6 +274,10 @@ public class ClassBundle implements OptionHandler {
public void addClassIdentifier(Identifier ident) { public void addClassIdentifier(Identifier ident) {
} }
public ClassPath getClassPath() {
return classPath;
}
public ClassIdentifier getClassIdentifier(String name) { public ClassIdentifier getClassIdentifier(String name) {
if (clazzCache.containsKey(name)) if (clazzCache.containsKey(name))
return (ClassIdentifier) clazzCache.get(name); return (ClassIdentifier) clazzCache.get(name);
@ -405,6 +413,13 @@ public class ClassBundle implements OptionHandler {
} }
public void run() { public void run() {
if (classPath == null) {
String cp = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar,
ClassPath.altPathSeparatorChar);
classPath = new ClassPath(cp);
}
if (analyzer == null) if (analyzer == null)
analyzer = new SimpleAnalyzer(); analyzer = new SimpleAnalyzer();
if (preTrafos == null) if (preTrafos == null)
@ -449,6 +464,7 @@ public class ClassBundle implements OptionHandler {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
basePackage.loadMatchingClasses(loading); basePackage.loadMatchingClasses(loading);
basePackage.initialize();
basePackage.applyPreserveRule(preserving); basePackage.applyPreserveRule(preserving);
System.err.println("Time used: "+(System.currentTimeMillis() - time)); System.err.println("Time used: "+(System.currentTimeMillis() - time));

@ -21,17 +21,21 @@ package jode.obfuscator;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.*; import jode.bytecode.*;
import jode.obfuscator.modules.ModifierMatcher; import jode.obfuscator.modules.ModifierMatcher;
import @COLLECTIONS@.Comparator; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Collection; import java.util.Comparator;
import @COLLECTIONS@.Collections; import java.util.Collection;
import @COLLECTIONS@.ArrayList; import java.util.Collections;
import @COLLECTIONS@.Arrays; import java.util.ArrayList;
import @COLLECTIONS@.Iterator; import java.util.Arrays;
import @COLLECTIONS@.List; import java.util.Iterator;
import @COLLECTIONS@.LinkedList; import java.util.List;
import @COLLECTIONS@.Map; import java.util.LinkedList;
import @COLLECTIONS@.Random; import java.util.Map;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.Random;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -52,6 +56,8 @@ public class ClassIdentifier extends Identifier {
List knownSubClasses = new LinkedList(); List knownSubClasses = new LinkedList();
List virtualReachables = new LinkedList(); List virtualReachables = new LinkedList();
boolean initialized;
public ClassIdentifier(PackageIdentifier pack, String fullName, public ClassIdentifier(PackageIdentifier pack, String fullName,
String name, ClassInfo info) { String name, ClassInfo info) {
super(name); super(name);
@ -265,8 +271,12 @@ public class ClassIdentifier extends Identifier {
} }
public boolean isSerializable() { public boolean isSerializable() {
return ClassInfo.forName("java.lang.Serializable") try {
.implementedBy(info); return info.getClassPath().getClassInfo("java.lang.Serializable")
.implementedBy(info);
} catch (IOException ex) {
throw new RuntimeException("Can't load full hierarchy of "+info);
}
} }
public boolean hasSUID() { public boolean hasSUID() {
return (findField("serialVersionUID", "J") != null); return (findField("serialVersionUID", "J") != null);
@ -331,6 +341,7 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = Main.getClassBundle() ClassIdentifier superident = Main.getClassBundle()
.getClassIdentifier(superclass.getName()); .getClassIdentifier(superclass.getName());
if (superident != null) { if (superident != null) {
superident.initClass();
for (Iterator i = superident.getMethodIdents().iterator(); for (Iterator i = superident.getMethodIdents().iterator();
i.hasNext(); ) { i.hasNext(); ) {
MethodIdentifier mid = (MethodIdentifier) i.next(); MethodIdentifier mid = (MethodIdentifier) i.next();
@ -369,7 +380,16 @@ public class ClassIdentifier extends Identifier {
} }
public void initClass() { public void initClass() {
info.loadInfo(info.FULLINFO); if (initialized)
return;
initialized = true;
try {
info.load(info.ALL);
} catch (IOException ex) {
throw new RuntimeException("Can't read class " + info.getName()
+ ": " + ex.getMessage());
}
FieldInfo[] finfos = info.getFields(); FieldInfo[] finfos = info.getFields();
MethodInfo[] minfos = info.getMethods(); MethodInfo[] minfos = info.getMethods();
@ -401,15 +421,11 @@ public class ClassIdentifier extends Identifier {
ifaceNames = new String[ifaces.length]; ifaceNames = new String[ifaces.length];
for (int i=0; i < ifaces.length; i++) { for (int i=0; i < ifaces.length; i++) {
ifaceNames[i] = ifaces[i].getName(); ifaceNames[i] = ifaces[i].getName();
ClassIdentifier ifaceident = Main.getClassBundle()
.getClassIdentifier(ifaceNames[i]);
initSuperClasses(ifaces[i]); initSuperClasses(ifaces[i]);
} }
if (info.getSuperclass() != null) { if (info.getSuperclass() != null) {
superName = info.getSuperclass().getName(); superName = info.getSuperclass().getName();
ClassIdentifier superident = Main.getClassBundle()
.getClassIdentifier(superName);
initSuperClasses(info.getSuperclass()); initSuperClasses(info.getSuperclass());
} }
@ -417,35 +433,27 @@ public class ClassIdentifier extends Identifier {
info.setSourceFile(null); info.setSourceFile(null);
} }
if ((Main.stripping & Main.STRIP_INNERINFO) != 0) { if ((Main.stripping & Main.STRIP_INNERINFO) != 0) {
info.setInnerClasses(new InnerClassInfo[0]); info.setClasses(null);
info.setOuterClasses(new InnerClassInfo[0]); info.setOuterClass(null);
info.setExtraClasses(new InnerClassInfo[0]); info.setExtraClasses(null);
} }
// load inner classes // load inner classes
InnerClassInfo[] innerClasses = info.getInnerClasses(); ClassInfo outerClass = info.getOuterClass();
InnerClassInfo[] outerClasses = info.getOuterClasses(); ClassInfo[] innerClasses = info.getClasses();
InnerClassInfo[] extraClasses = info.getExtraClasses(); ClassInfo[] extraClasses = info.getExtraClasses();
if (outerClasses != null) { if (outerClass != null)
for (int i=0; i < outerClasses.length; i++) { Main.getClassBundle().getClassIdentifier(outerClass.getName());
if (outerClasses[i].outer != null) {
Main.getClassBundle()
.getClassIdentifier(outerClasses[i].outer);
}
}
}
if (innerClasses != null) { if (innerClasses != null) {
for (int i=0; i < innerClasses.length; i++) { for (int i=0; i < innerClasses.length; i++) {
Main.getClassBundle() Main.getClassBundle()
.getClassIdentifier(innerClasses[i].inner); .getClassIdentifier(innerClasses[i].getName());
} }
} }
if (extraClasses != null) { if (extraClasses != null) {
for (int i=0; i < extraClasses.length; i++) { for (int i=0; i < extraClasses.length; i++) {
Main.getClassBundle() Main.getClassBundle()
.getClassIdentifier(extraClasses[i].inner); .getClassIdentifier(extraClasses[i].getName());
if (extraClasses[i].outer != null)
Main.getClassBundle()
.getClassIdentifier(extraClasses[i].outer);
} }
} }
} }
@ -499,142 +507,66 @@ public class ClassIdentifier extends Identifier {
} }
public void transformInnerClasses() { public void transformInnerClasses() {
InnerClassInfo[] outerClasses = info.getOuterClasses(); ClassInfo outerClass = info.getOuterClass();
if (outerClasses != null) { if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
int newOuterCount = outerClasses.length; while (outerClass != null) {
if ((Main.stripping & Main.STRIP_UNREACH) != 0) { ClassIdentifier outerIdent = Main.getClassBundle()
for (int i=0; i < outerClasses.length; i++) { .getClassIdentifier(outerClass.getName());
if (outerClasses[i].outer != null) { if (outerIdent != null && outerIdent.isReachable())
ClassIdentifier outerIdent = Main.getClassBundle() break;
.getClassIdentifier(outerClasses[i].outer);
if (outerIdent != null && !outerIdent.isReachable())
newOuterCount--;
}
}
}
if (newOuterCount == 0) {
info.setOuterClasses(null);
} else {
InnerClassInfo[] newOuters = new InnerClassInfo[newOuterCount];
int pos = 0;
String lastClass = getFullAlias();
for (int i=0; i<outerClasses.length; i++) {
ClassIdentifier outerIdent = outerClasses[i].outer != null
? (Main.getClassBundle()
.getClassIdentifier(outerClasses[i].outer))
: null;
if (outerIdent != null && !outerIdent.isReachable())
continue;
String inner = lastClass;
String outer = outerIdent == null
? outerClasses[i].outer
: outerIdent.getFullAlias();
String name = outerClasses[i].name == null ? null
: ((outer != null && inner.startsWith(outer+"$"))
? inner.substring(outer.length()+1)
: inner.substring(inner.lastIndexOf('.')+1));
newOuters[pos++] = new InnerClassInfo
(inner, outer, name, outerClasses[i].modifiers);
lastClass = outer;
}
info.setOuterClasses(newOuters);
} }
} }
info.setOuterClass(outerClass);
InnerClassInfo[] innerClasses = info.getInnerClasses(); ClassInfo[] innerClasses = info.getClasses();
if (innerClasses != null) { if (innerClasses != null) {
int newInnerCount = innerClasses.length; int newInnerCount = innerClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) { if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < innerClasses.length; i++) { for (int i=0; i < innerClasses.length; i++) {
ClassIdentifier innerIdent = Main.getClassBundle() ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(innerClasses[i].inner); .getClassIdentifier(innerClasses[i].getName());
if (innerIdent != null && !innerIdent.isReachable()) if (innerIdent != null && !innerIdent.isReachable()) {
innerClasses[i] = null;
newInnerCount--; newInnerCount--;
}
} }
} }
if (newInnerCount == 0) { if (newInnerCount == 0) {
info.setInnerClasses(null); info.setClasses(null);
} else { } else {
InnerClassInfo[] newInners = new InnerClassInfo[newInnerCount]; ClassInfo[] newInners = new ClassInfo[newInnerCount];
int pos = 0; int pos = 0;
for (int i=0; i<innerClasses.length; i++) { for (int i=0; i<innerClasses.length; i++) {
ClassIdentifier innerIdent = Main.getClassBundle() if (innerClasses[i] != null)
.getClassIdentifier(innerClasses[i].inner); newInners[pos++] = innerClasses[i];
if (innerIdent != null
&& (Main.stripping & Main.STRIP_UNREACH) != 0
&& !innerIdent.isReachable())
continue;
String inner = innerIdent == null
? innerClasses[i].inner
: innerIdent.getFullAlias();
String outer = getFullAlias();
String name = innerClasses[i].name == null ? null
: ((outer != null && inner.startsWith(outer+"$"))
? inner.substring(outer.length()+1)
: inner.substring(inner.lastIndexOf('.')+1));
newInners[pos++] = new InnerClassInfo
(inner, outer, name, innerClasses[i].modifiers);
} }
info.setInnerClasses(newInners); info.setClasses(newInners);
} }
} }
InnerClassInfo[] extraClasses = info.getExtraClasses(); ClassInfo[] extraClasses = info.getExtraClasses();
if (extraClasses != null) { if (extraClasses != null) {
int newExtraCount = extraClasses.length; int newExtraCount = extraClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) { if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < extraClasses.length; i++) { for (int i=0; i < extraClasses.length; i++) {
ClassIdentifier outerIdent = extraClasses[i].outer != null ClassIdentifier extraIdent = Main.getClassBundle()
? (Main.getClassBundle() .getClassIdentifier(extraClasses[i].getName());
.getClassIdentifier(extraClasses[i].outer)) if (extraIdent != null && !extraIdent.isReachable()) {
: null; extraClasses[i] = null;
ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].inner);
if ((outerIdent != null && !outerIdent.isReachable())
|| (innerIdent != null && !innerIdent.isReachable()))
newExtraCount--; newExtraCount--;
}
} }
} }
if (newExtraCount == 0) { if (newExtraCount == 0) {
info.setExtraClasses(null); info.setExtraClasses(null);
} else { } else {
InnerClassInfo[] newExtras = newExtraCount > 0 ClassInfo[] newExtras = new ClassInfo[newExtraCount];
? new InnerClassInfo[newExtraCount] : null;
int pos = 0; int pos = 0;
for (int i=0; i<extraClasses.length; i++) { for (int i=0; i<extraClasses.length; i++) {
ClassIdentifier outerIdent = extraClasses[i].outer != null if (extraClasses[i] != null)
? (Main.getClassBundle() newExtras[pos++] = extraClasses[i];
.getClassIdentifier(extraClasses[i].outer))
: null;
ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].inner);
if (innerIdent != null && !innerIdent.isReachable())
continue;
if (outerIdent != null && !outerIdent.isReachable())
continue;
String inner = innerIdent == null
? extraClasses[i].inner
: innerIdent.getFullAlias();
String outer = outerIdent == null
? extraClasses[i].outer
: outerIdent.getFullAlias();
String name = extraClasses[i].name == null ? null
: ((outer != null && inner.startsWith(outer+"$"))
? inner.substring(outer.length()+1)
: inner.substring(inner.lastIndexOf('.')+1));
newExtras[pos++] = new InnerClassInfo
(inner, outer, name, extraClasses[i].modifiers);
} }
info.setExtraClasses(newExtras); info.setExtraClasses(newExtras);
} }
@ -657,6 +589,8 @@ public class ClassIdentifier extends Identifier {
|| ident.isReachable()) { || ident.isReachable()) {
ident.doTransformations(); ident.doTransformations();
newFields.add(ident.info); newFields.add(ident.info);
} else if (GlobalOptions.verboseLevel > 2) {
GlobalOptions.err.println("Field "+ ident+" not reachable");
} }
} }
for (Iterator i = methodIdents.iterator(); i.hasNext(); ) { for (Iterator i = methodIdents.iterator(); i.hasNext(); ) {
@ -665,6 +599,8 @@ public class ClassIdentifier extends Identifier {
|| ident.isReachable()) { || ident.isReachable()) {
ident.doTransformations(); ident.doTransformations();
newMethods.add(ident.info); newMethods.add(ident.info);
} else if (GlobalOptions.verboseLevel > 2) {
GlobalOptions.err.println("Method "+ ident+" not reachable");
} }
} }
@ -812,8 +748,8 @@ public class ClassIdentifier extends Identifier {
* overriden but hidden. We must only take care, that the * overriden but hidden. We must only take care, that the
* reference of every getfield/putfield opcode points to the * reference of every getfield/putfield opcode points to the
* exact class, afterwards we can use doubled name as much as * exact class, afterwards we can use doubled name as much as
* we want (event the decompiler can handle this). */ * we want (even the decompiler can handle this).
*/
ModifierMatcher mm = ModifierMatcher.allowAll; ModifierMatcher mm = ModifierMatcher.allowAll;
if (containsFieldAliasDirectly(newAlias, typeSig, mm)) if (containsFieldAliasDirectly(newAlias, typeSig, mm))

@ -27,8 +27,10 @@ import jode.bytecode.TypeSignature;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import @COLLECTIONS@.HashSet; import java.util.Set;
import java.util.HashSet;
///#enddef
public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {

@ -20,11 +20,13 @@
package jode.obfuscator; package jode.obfuscator;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import jode.bytecode.*; import jode.bytecode.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Collections; import java.util.Collection;
import @COLLECTIONS@.Iterator; import java.util.Collections;
import @COLLECTIONS@.HashSet; import java.util.Iterator;
import @COLLECTIONS@.Map; import java.util.HashSet;
import java.util.Map;
///#enddef
public class FieldIdentifier extends Identifier{ public class FieldIdentifier extends Identifier{

@ -20,8 +20,10 @@
package jode.obfuscator; package jode.obfuscator;
import jode.GlobalOptions; import jode.GlobalOptions;
import java.io.*; import java.io.*;
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Map;
import java.util.Iterator;
///#enddef
public abstract class Identifier { public abstract class Identifier {
/** /**

@ -19,8 +19,10 @@
package jode.obfuscator; package jode.obfuscator;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collections;
import java.util.Iterator;
///#enddef
public class LocalIdentifier extends Identifier { public class LocalIdentifier extends Identifier {
String name; String name;

@ -18,8 +18,6 @@
*/ */
package jode.obfuscator; package jode.obfuscator;
import jode.bytecode.ClassInfo;
import jode.bytecode.SearchPath;
import jode.GlobalOptions; import jode.GlobalOptions;
import gnu.getopt.LongOpt; import gnu.getopt.LongOpt;
@ -30,7 +28,9 @@ import java.io.PrintWriter;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.IOException; import java.io.IOException;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import java.util.Collections;
///#enddef
public class Main { public class Main {
public static boolean swapOrder = false; public static boolean swapOrder = false;
@ -63,7 +63,7 @@ public class Main {
public static void usage() { public static void usage() {
PrintWriter err = GlobalOptions.err; PrintWriter err = GlobalOptions.err;
err.println("usage: jode.Obfuscator flags* script"); err.println("usage: jode.obfuscator.Main flags* script");
err.println(" -h, --help "+ err.println(" -h, --help "+
"show this information."); "show this information.");
err.println(" -V, --version "+ err.println(" -V, --version "+

@ -2,11 +2,9 @@
SUBDIRS = modules SUBDIRS = modules
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -22,9 +22,11 @@ import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.*; import jode.bytecode.*;
import @COLLECTIONS@.Collections; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collections;
import @COLLECTIONS@.Map; import java.util.Iterator;
import java.util.Map;
///#enddef
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.BitSet; import java.util.BitSet;

@ -19,7 +19,9 @@
package jode.obfuscator; package jode.obfuscator;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public interface OptionHandler { public interface OptionHandler {
public void setOption(String option, Collection values) public void setOption(String option, Collection values)

@ -29,14 +29,16 @@ import java.util.Enumeration;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import @COLLECTIONS@.HashMap; import java.util.Map;
import @COLLECTIONS@.Iterator; import java.util.HashMap;
import @COLLECTIONS@.List; import java.util.Iterator;
import @COLLECTIONS@.ArrayList; import java.util.List;
import @COLLECTIONS@.Random; import java.util.ArrayList;
import @COLLECTIONS@.Arrays; import java.util.Random;
import @COLLECTIONS@.Collections; import java.util.Arrays;
import java.util.Collections;
///#enddef
public class PackageIdentifier extends Identifier { public class PackageIdentifier extends Identifier {
ClassBundle bundle; ClassBundle bundle;
@ -45,6 +47,7 @@ public class PackageIdentifier extends Identifier {
String fullName; String fullName;
boolean loadOnDemand; boolean loadOnDemand;
boolean initialized = false;
Map loadedClasses; Map loadedClasses;
List swappedClasses; List swappedClasses;
Random rand = new Random(); Random rand = new Random();
@ -93,7 +96,8 @@ public class PackageIdentifier extends Identifier {
ident.setLoadOnDemand(); ident.setLoadOnDemand();
} else { } else {
ClassIdentifier ident = new ClassIdentifier ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz, ClassInfo.forName(subFull)); (this, subFull, subclazz,
bundle.getClassPath().getClassInfo(subFull));
if (GlobalOptions.verboseLevel > 1) if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("preloading Class " GlobalOptions.err.println("preloading Class "
@ -101,7 +105,6 @@ public class PackageIdentifier extends Identifier {
loadedClasses.put(subclazz, ident); loadedClasses.put(subclazz, ident);
swappedClasses = null; swappedClasses = null;
bundle.addClassIdentifier(ident); bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass();
} }
} }
// Everything is loaded, we don't need to load on demand anymore. // Everything is loaded, we don't need to load on demand anymore.
@ -109,78 +112,6 @@ public class PackageIdentifier extends Identifier {
} }
} }
public Identifier getIdentifier(String name) {
if (loadOnDemand) {
Identifier ident = loadClass(name);
return ident;
}
int index = name.indexOf('.');
if (index == -1)
return (Identifier) loadedClasses.get(name);
else {
PackageIdentifier pack = (PackageIdentifier)
loadedClasses.get(name.substring(0, index));
if (pack != null)
return pack.getIdentifier(name.substring(index+1));
else
return null;
}
}
public Identifier loadClass(String name) {
int index = name.indexOf('.');
if (index == -1) {
Identifier ident = (Identifier) loadedClasses.get(name);
if (ident == null) {
String subFull =
(fullName.length() > 0) ? fullName + "."+ name : name;
subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) {
PackageIdentifier pack
= new PackageIdentifier(bundle, this, subFull, name);
loadedClasses.put(name, pack);
swappedClasses = null;
pack.setLoadOnDemand();
ident = pack;
} else if (!ClassInfo.exists(subFull)) {
GlobalOptions.err.println("Warning: Can't find class "
+ subFull);
Thread.dumpStack();
} else {
ident = new ClassIdentifier(this, subFull, name,
ClassInfo.forName(subFull));
loadedClasses.put(name, ident);
swappedClasses = null;
bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass();
}
}
return ident;
} else {
String subpack = name.substring(0, index);
PackageIdentifier pack =
(PackageIdentifier) loadedClasses.get(subpack);
if (pack == null) {
String subFull = (fullName.length() > 0)
? fullName + "."+ subpack : subpack;
subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) {
pack = new PackageIdentifier(bundle, this,
subFull, subpack);
loadedClasses.put(subpack, pack);
swappedClasses = null;
if (loadOnDemand)
pack.setLoadOnDemand();
}
}
if (pack != null)
return pack.loadClass(name.substring(index+1));
else
return null;
}
}
public void loadMatchingClasses(IdentifierMatcher matcher) { public void loadMatchingClasses(IdentifierMatcher matcher) {
String component = matcher.getNextComponent(this); String component = matcher.getNextComponent(this);
if (component != null) { if (component != null) {
@ -201,12 +132,14 @@ public class PackageIdentifier extends Identifier {
if (GlobalOptions.verboseLevel > 1) if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("loading Class " +subFull); GlobalOptions.err.println("loading Class " +subFull);
ident = new ClassIdentifier(this, subFull, component, ident = new ClassIdentifier(this, subFull, component,
ClassInfo.forName(subFull)); bundle.getClassPath()
.getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) { if (loadOnDemand || matcher.matches(ident)) {
loadedClasses.put(component, ident); loadedClasses.put(component, ident);
if (initialized)
((ClassIdentifier) ident).initClass();
swappedClasses = null; swappedClasses = null;
bundle.addClassIdentifier(ident); bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass();
} }
} else { } else {
GlobalOptions.err.println GlobalOptions.err.println
@ -250,7 +183,7 @@ public class PackageIdentifier extends Identifier {
} else { } else {
ClassIdentifier ident = new ClassIdentifier ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz, (this, subFull, subclazz,
ClassInfo.forName(subFull)); bundle.getClassPath().getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) { if (loadOnDemand || matcher.matches(ident)) {
if (GlobalOptions.verboseLevel > 1) if (GlobalOptions.verboseLevel > 1)
@ -259,7 +192,8 @@ public class PackageIdentifier extends Identifier {
loadedClasses.put(subclazz, ident); loadedClasses.put(subclazz, ident);
swappedClasses = null; swappedClasses = null;
bundle.addClassIdentifier(ident); bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass(); if (initialized)
((ClassIdentifier) ident).initClass();
} }
} }
} }
@ -280,6 +214,89 @@ public class PackageIdentifier extends Identifier {
} }
} }
public void initialize() {
for (Iterator i = getChilds(); i.hasNext(); ) {
Identifier ident = (Identifier) i.next();
if (ident instanceof ClassIdentifier)
((ClassIdentifier) ident).initClass();
else
((PackageIdentifier) ident).initialize();
}
initialized = true;
}
public Identifier getIdentifier(String name) {
if (loadOnDemand) {
return loadClass(name);
}
int index = name.indexOf('.');
if (index == -1) {
return (Identifier) loadedClasses.get(name);
} else {
PackageIdentifier pack = (PackageIdentifier)
loadedClasses.get(name.substring(0, index));
if (pack != null)
return pack.getIdentifier(name.substring(index+1));
else
return null;
}
}
private Identifier loadClass(String name) {
int index = name.indexOf('.');
if (index == -1) {
Identifier ident = (Identifier) loadedClasses.get(name);
if (ident == null) {
String subFull =
(fullName.length() > 0) ? fullName + "."+ name : name;
subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) {
PackageIdentifier pack
= new PackageIdentifier(bundle, this, subFull, name);
loadedClasses.put(name, pack);
swappedClasses = null;
pack.setLoadOnDemand();
ident = pack;
} else if (!ClassInfo.exists(subFull)) {
GlobalOptions.err.println("Warning: Can't find class "
+ subFull);
Thread.dumpStack();
} else {
ident = new ClassIdentifier(this, subFull, name,
bundle.getClassPath()
.getClassInfo(subFull));
loadedClasses.put(name, ident);
((ClassIdentifier) ident).initClass();
swappedClasses = null;
bundle.addClassIdentifier(ident);
}
}
return ident;
} else {
String subpack = name.substring(0, index);
PackageIdentifier pack =
(PackageIdentifier) loadedClasses.get(subpack);
if (pack == null) {
String subFull = (fullName.length() > 0)
? fullName + "."+ subpack : subpack;
subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) {
pack = new PackageIdentifier(bundle, this,
subFull, subpack);
loadedClasses.put(subpack, pack);
swappedClasses = null;
if (loadOnDemand)
pack.setLoadOnDemand();
}
}
if (pack != null)
return pack.loadClass(name.substring(index+1));
else
return null;
}
}
public void applyPreserveRule(IdentifierMatcher preserveRule) { public void applyPreserveRule(IdentifierMatcher preserveRule) {
if (loadOnDemand) if (loadOnDemand)
loadMatchingClasses(preserveRule); loadMatchingClasses(preserveRule);

@ -18,7 +18,9 @@
*/ */
package jode.obfuscator; package jode.obfuscator;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
public interface Renamer { public interface Renamer {

@ -23,8 +23,10 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.LinkedList; import java.util.Collection;
import java.util.LinkedList;
///#enddef
public class ScriptParser { public class ScriptParser {
static int NO_TOKEN = -2; static int NO_TOKEN = -2;

@ -19,12 +19,16 @@
package jode.obfuscator; package jode.obfuscator;
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import @COLLECTIONS@.TreeMap; import java.util.Map;
import @COLLECTIONS@.Iterator; import java.util.TreeMap;
import java.util.Iterator;
///#enddef
///#ifndef JDK12 ///#ifndef JDK12
import @COLLECTIONS@.Comparator; ///#def COLLECTIONS java.util
///import java.util.Comparator;
///#enddef
///#endif ///#endif
import java.io.InputStream; import java.io.InputStream;
@ -37,17 +41,17 @@ import java.io.IOException;
public class TranslationTable extends TreeMap { public class TranslationTable extends TreeMap {
///#ifndef JDK12 ///#ifndef JDK12
public TranslationTable() { /// public TranslationTable() {
super(createStringComparator()); /// super(createStringComparator());
} /// }
///
private static Comparator createStringComparator() { /// private static Comparator createStringComparator() {
return new Comparator() { /// return new Comparator() {
public int compare(Object o1, Object o2) { /// public int compare(Object o1, Object o2) {
return ((String) o1).compareTo((String) o2); /// return ((String) o1).compareTo((String) o2);
} /// }
}; /// };
} /// }
///#endif ///#endif
public void load(InputStream in) throws IOException { public void load(InputStream in) throws IOException {

@ -30,14 +30,16 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.BitSet; import java.util.BitSet;
import @COLLECTIONS@.Arrays; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Collection; import java.util.Arrays;
import @COLLECTIONS@.HashSet; import java.util.Collection;
import @COLLECTIONS@.Set; import java.util.HashSet;
import @COLLECTIONS@.HashMap; import java.util.Set;
import @COLLECTIONS@.Map; import java.util.HashMap;
import @COLLECTIONS@.Iterator; import java.util.Map;
import @COLLECTIONS@.ListIterator; import java.util.Iterator;
import java.util.ListIterator;
///#enddef
/** /**
* Analyze the code, assuming every field that is not yet written to * Analyze the code, assuming every field that is not yet written to
@ -550,7 +552,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
/* Arrays don't define new methods (well clone(), /* Arrays don't define new methods (well clone(),
* but that can be ignored). * but that can be ignored).
*/ */
clazz = ClassInfo.javaLangObject; clazz = ClassInfo.forName("java.lang.Object");
} else { } else {
clazz = ClassInfo.forName clazz = ClassInfo.forName
(clName.substring(1, clName.length()-1) (clName.substring(1, clName.length()-1)

@ -19,9 +19,13 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collection;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
public class KeywordRenamer implements Renamer, OptionHandler { public class KeywordRenamer implements Renamer, OptionHandler {
String keywords[]; String keywords[];

@ -24,8 +24,10 @@ import jode.obfuscator.*;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import @COLLECTIONS@.ListIterator; import java.util.Iterator;
import java.util.ListIterator;
///#enddef
/** /**
* This class takes some bytecode and tries to minimize the number * This class takes some bytecode and tries to minimize the number

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -26,8 +26,10 @@ import jode.obfuscator.MethodIdentifier;
import jode.obfuscator.OptionHandler; import jode.obfuscator.OptionHandler;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collection;
import java.util.Iterator;
///#enddef
public class ModifierMatcher implements IdentifierMatcher, OptionHandler, Cloneable { public class ModifierMatcher implements IdentifierMatcher, OptionHandler, Cloneable {
static final int PUBLIC = Modifier.PUBLIC; static final int PUBLIC = Modifier.PUBLIC;
@ -109,7 +111,7 @@ public class ModifierMatcher implements IdentifierMatcher, OptionHandler, Clonea
: str.equals("NATIVE") ? Modifier.NATIVE : str.equals("NATIVE") ? Modifier.NATIVE
: str.equals("STATIC") ? Modifier.STATIC : str.equals("STATIC") ? Modifier.STATIC
///#ifdef JDK12 ///#ifdef JDK12
/// : str.equals("STRICT") ? Modifier.STRICT : str.equals("STRICT") ? Modifier.STRICT
///#endif ///#endif
: str.equals("SYNCHRONIZED") ? Modifier.SYNCHRONIZED : str.equals("SYNCHRONIZED") ? Modifier.SYNCHRONIZED
: str.equals("TRANSIENT") ? Modifier.TRANSIENT : str.equals("TRANSIENT") ? Modifier.TRANSIENT

@ -19,7 +19,9 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public class MultiIdentifierMatcher implements IdentifierMatcher, OptionHandler { public class MultiIdentifierMatcher implements IdentifierMatcher, OptionHandler {
/** /**

@ -20,12 +20,16 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Set; import java.util.Collection;
import @COLLECTIONS@.HashSet; import java.util.Set;
import @COLLECTIONS@.Iterator; import java.util.HashSet;
import @COLLECTIONS@.Random; import java.util.Iterator;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.Random;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
public class NameSwapper implements Renamer { public class NameSwapper implements Renamer {

@ -23,7 +23,9 @@ import jode.obfuscator.*;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import @COLLECTIONS@.ListIterator; ///#def COLLECTIONS java.util
import java.util.ListIterator;
///#enddef
public class RemovePopAnalyzer implements CodeTransformer, Opcodes { public class RemovePopAnalyzer implements CodeTransformer, Opcodes {
public RemovePopAnalyzer() { public RemovePopAnalyzer() {

@ -21,7 +21,9 @@ package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public class SerializePreserver implements IdentifierMatcher { public class SerializePreserver implements IdentifierMatcher {
boolean onlySUID = true; boolean onlySUID = true;

@ -29,8 +29,10 @@ import jode.obfuscator.Identifier;
import jode.obfuscator.*; import jode.obfuscator.*;
import jode.GlobalOptions; import jode.GlobalOptions;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import @COLLECTIONS@.ListIterator; import java.util.Iterator;
import java.util.ListIterator;
///#enddef
public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
@ -52,7 +54,7 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
/* Arrays don't define new methods (well clone(), /* Arrays don't define new methods (well clone(),
* but that can be ignored). * but that can be ignored).
*/ */
clazz = ClassInfo.javaLangObject; clazz = ClassInfo.forName("java.lang.Object");
} else { } else {
clazz = ClassInfo.forName clazz = ClassInfo.forName
(clName.substring(1, clName.length()-1) (clName.substring(1, clName.length()-1)
@ -125,7 +127,7 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
if (instr.getOpcode() == opc_putstatic if (instr.getOpcode() == opc_putstatic
|| instr.getOpcode() == opc_putfield) { || instr.getOpcode() == opc_putfield) {
FieldIdentifier fi = (FieldIdentifier) ident; FieldIdentifier fi = (FieldIdentifier) ident;
if (fi != null && !fi.isNotConstant()) if (!fi.isNotConstant())
fi.setNotConstant(); fi.setNotConstant();
} else if (instr.getOpcode() == opc_invokevirtual } else if (instr.getOpcode() == opc_invokevirtual
|| instr.getOpcode() == opc_invokeinterface) { || instr.getOpcode() == opc_invokeinterface) {

@ -19,9 +19,13 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.Collection;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
public class StrongRenamer implements Renamer, OptionHandler { public class StrongRenamer implements Renamer, OptionHandler {
static String[] idents = { static String[] idents = {

@ -20,8 +20,12 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
public class UniqueRenamer implements Renamer { public class UniqueRenamer implements Renamer {
static int serialnr = 0; static int serialnr = 0;

@ -19,7 +19,9 @@
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.obfuscator.*; import jode.obfuscator.*;
import @COLLECTIONS@.Collection; ///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public class WildCard implements IdentifierMatcher, OptionHandler { public class WildCard implements IdentifierMatcher, OptionHandler {

@ -20,19 +20,25 @@
package jode.swingui; package jode.swingui;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import @JAVAX_SWING@.JProgressBar; ///#def JAVAX_SWING javax.swing
import @JAVAX_SWING@.tree.TreeModel; import javax.swing.JProgressBar;
import @JAVAX_SWING@.tree.TreePath; import javax.swing.tree.TreeModel;
import @JAVAX_SWING@.event.TreeModelListener; import javax.swing.tree.TreePath;
import @JAVAX_SWING@.event.TreeModelEvent; import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;
import @COLLECTIONEXTRA@.Comparable; //#enddef
import @COLLECTIONS@.TreeSet;
import @COLLECTIONS@.Collection; ///#def COLLECTIONEXTRA java.lang
import @COLLECTIONS@.Iterator; import java.lang.Comparable;
import @COLLECTIONS@.HashMap; ///#enddef
import @COLLECTIONS@.Set; ///#def COLLECTIONS java.util
import @COLLECTIONS@.HashSet; import java.util.TreeSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
///#enddef
import java.util.Enumeration; import java.util.Enumeration;
@ -122,8 +128,8 @@ public class HierarchyTreeModel implements TreeModel, Runnable {
if (ClassInfo.isPackage(fqn)) { if (ClassInfo.isPackage(fqn)) {
count = readPackage(depth, classes, fqn, count); count = readPackage(depth, classes, fqn, count);
} else { } else {
TreeElement elem = handleClass(classes, TreeElement elem = handleClass
ClassInfo.forName(fqn)); (classes, main.getClassPath().getClassInfo(fqn));
if (progressBar != null) if (progressBar != null)
progressBar.setValue(++count); progressBar.setValue(++count);
@ -240,7 +246,7 @@ public class HierarchyTreeModel implements TreeModel, Runnable {
return new TreePath(root); return new TreePath(root);
int length = 2; int length = 2;
ClassInfo ci = ClassInfo.forName(fullName); ClassInfo ci = main.getClassPath().getClassInfo(fullName);
while (ci.getSuperclass() != null) { while (ci.getSuperclass() != null) {
length++; length++;
ci = ci.getSuperclass(); ci = ci.getSuperclass();
@ -251,7 +257,7 @@ public class HierarchyTreeModel implements TreeModel, Runnable {
int nr = 0; int nr = 0;
next_component: next_component:
while (nr < length-1) { while (nr < length-1) {
ci = ClassInfo.forName(fullName); ci = main.getClassPath().getClassInfo(fullName);
for (int i=2; i < length - nr; i++) for (int i=2; i < length - nr; i++)
ci = ci.getSuperclass(); ci = ci.getSuperclass();
Iterator iter = path[nr].getChilds().iterator(); Iterator iter = path[nr].getChilds().iterator();

@ -19,12 +19,15 @@
package jode.swingui; package jode.swingui;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassPath;
import jode.decompiler.Decompiler; import jode.decompiler.Decompiler;
import jode.decompiler.ProgressListener; import jode.decompiler.ProgressListener;
import @JAVAX_SWING@.*; ///#def JAVAX_SWING javax.swing
import @JAVAX_SWING@.event.*; import javax.swing.*;
import @JAVAX_SWING@.tree.*; import javax.swing.event.*;
import javax.swing.tree.*;
///#enddef
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
@ -41,6 +44,7 @@ public class Main
JTextArea sourcecodeArea, errorArea; JTextArea sourcecodeArea, errorArea;
Thread decompileThread; Thread decompileThread;
String currentClassPath, lastClassName; String currentClassPath, lastClassName;
ClassPath classPath;
JProgressBar progressBar; JProgressBar progressBar;
@ -281,12 +285,17 @@ public class Main
frame.setJMenuBar(bar); frame.setJMenuBar(bar);
} }
public ClassPath getClassPath() {
return classPath;
}
ClassPath reflectClassPath = new ClassPath("reflection:");
public void setClassPath(String classpath) { public void setClassPath(String classpath) {
if (classpath == null || classpath.length() == 0) if (classpath == null || classpath.length() == 0)
classpath = "."; classpath = ".";
currentClassPath = classpath; currentClassPath = classpath;
jode.bytecode.ClassInfo.setClassPath(classpath); classPath = new ClassPath(classpath, reflectClassPath);
decompiler.setClassPath(classpath); decompiler.setClassPath(classPath);
if (classTree != null) if (classTree != null)
classTree.clearSelection(); classTree.clearSelection();
if (packModel != null) if (packModel != null)
@ -326,15 +335,30 @@ public class Main
} }
} }
public static void usage() {
PrintWriter err = GlobalOptions.err;
err.println("usage: jode.swingui.Main flags* script");
err.println(" -h, --help "+
"show this information.");
err.println(" -c, --cp <path>, --classpath <path> "+
"search for classes in specified classpath.");
err.println(" "+
"The directories should be separated by commas.");
}
public static void main(String[] params) { public static void main(String[] params) {
String cp = System.getProperty("java.class.path", ""); String cp = System.getProperty("java.class.path", "");
cp = cp.replace(File.pathSeparatorChar, cp = cp.replace(File.pathSeparatorChar,
Decompiler.altPathSeparatorChar); Decompiler.altPathSeparatorChar);
for (int i=0; i<params.length; i++) { for (int i=0; i<params.length; i++) {
if (params[i].equals("--classpath")) if (params[i].equals("--classpath")
|| params[i].equals("--cp")
|| params[i].equals("-c"))
cp = params[++i]; cp = params[++i];
else else {
usage();
return; return;
}
} }
Main win = new Main(cp); Main win = new Main(cp);
win.show(); win.show();

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -20,19 +20,26 @@
package jode.swingui; package jode.swingui;
import jode.decompiler.Options; import jode.decompiler.Options;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath;
import @JAVAX_SWING@.tree.TreeModel;
import @JAVAX_SWING@.tree.TreePath; ///#def JAVAX_SWING javax.swing
import @JAVAX_SWING@.event.TreeModelListener; import javax.swing.tree.TreeModel;
import @JAVAX_SWING@.event.TreeModelEvent; import javax.swing.tree.TreePath;
import javax.swing.event.TreeModelListener;
import @COLLECTIONEXTRA@.Comparable; import javax.swing.event.TreeModelEvent;
import @COLLECTIONS@.Arrays; ///#enddef
import @COLLECTIONS@.TreeSet;
import @COLLECTIONS@.HashSet; ///#def COLLECTIONEXTRA java.lang
import @COLLECTIONS@.Set; import java.lang.Comparable;
import @COLLECTIONS@.HashMap; ///#enddef
import @COLLECTIONS@.Map; ///#def COLLECTIONS java.util
import java.util.Arrays;
import java.util.TreeSet;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
///#enddef
import java.util.Enumeration; import java.util.Enumeration;
@ -109,20 +116,22 @@ public class PackagesTreeModel implements TreeModel {
} }
public TreeElement[] getChildrens(TreeElement parent) { public TreeElement[] getChildrens(TreeElement parent) {
ClassPath classPath = main.getClassPath();
TreeElement[] result = TreeElement[] result =
(TreeElement[]) cachedChildrens.get(parent); (TreeElement[]) cachedChildrens.get(parent);
if (result == null) { if (result == null) {
TreeSet v = new TreeSet(); TreeSet v = new TreeSet();
String prefix = parent == root ? "" : parent.getFullName() + "."; String prefix = parent == root ? "" : parent.getFullName() + ".";
Enumeration enum = Enumeration enum
ClassInfo.getClassesAndPackages(parent.getFullName()); = classPath.listClassesAndPackages(parent.getFullName());
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
//insert sorted and remove double elements; //insert sorted and remove double elements;
String name = (String)enum.nextElement(); String name = (String)enum.nextElement();
String fqn = prefix + name; String fqn = prefix + name;
boolean isClass = !ClassInfo.isPackage(fqn); boolean isClass = !classPath.isPackage(fqn);
if (isClass && Options.skipClass(ClassInfo.forName(fqn))) if (isClass
&& Options.skipClass(classPath.getClassInfo(fqn)))
continue; continue;
TreeElement newElem = new TreeElement(prefix, name, isClass); TreeElement newElem = new TreeElement(prefix, name, isClass);
v.add(newElem); v.add(newElem);

@ -20,6 +20,7 @@
package jode.type; package jode.type;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import java.util.Vector; import java.util.Vector;
import java.io.IOException;
/** /**
* This type represents an array type. * This type represents an array type.
@ -80,6 +81,11 @@ public class ArrayType extends ReferenceType {
if (bottom.getTypeCode() == TC_CLASS) { if (bottom.getTypeCode() == TC_CLASS) {
ClassInterfacesType bottomCIT = (ClassInterfacesType)bottom; ClassInterfacesType bottomCIT = (ClassInterfacesType)bottom;
// int len = arrayIfacesStrs.length;
// ClassInfo[] arrayIfaces = new ClassInfo[len];
// for (int i=0; i< arrayIfacesStrs.length; i++)
// arrayIfaces[i]
// = bottomCIT.classPath.getClassInfo(arrayIfacesStrs[i]);
if (bottomCIT.clazz == null if (bottomCIT.clazz == null
&& implementsAllIfaces(null, arrayIfaces, bottomCIT.ifaces)) && implementsAllIfaces(null, arrayIfaces, bottomCIT.ifaces))
return tRange(bottomCIT, this); return tRange(bottomCIT, this);
@ -149,6 +155,7 @@ public class ArrayType extends ReferenceType {
* First get all interfaces of this.clazz and this.ifaces. * First get all interfaces of this.clazz and this.ifaces.
*/ */
Vector newIfaces = new Vector(); Vector newIfaces = new Vector();
try {
iface_loop: iface_loop:
for (int i=0; i < arrayIfaces.length; i++) { for (int i=0; i < arrayIfaces.length; i++) {
/* Now consider each array interface. If any clazz or /* Now consider each array interface. If any clazz or
@ -169,6 +176,9 @@ public class ArrayType extends ReferenceType {
ClassInfo[] ifaceArray = new ClassInfo[newIfaces.size()]; ClassInfo[] ifaceArray = new ClassInfo[newIfaces.size()];
newIfaces.copyInto(ifaceArray); newIfaces.copyInto(ifaceArray);
return ClassInterfacesType.create(null, ifaceArray); return ClassInterfacesType.create(null, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
}
} }
return tError; return tError;
} }

@ -22,6 +22,7 @@ import jode.bytecode.ClassInfo;
import java.util.Vector; import java.util.Vector;
import java.util.Stack; import java.util.Stack;
import java.util.Hashtable; import java.util.Hashtable;
import java.io.IOException;
/** /**
* This class represents a type aproximation, consisting of multiple * This class represents a type aproximation, consisting of multiple
@ -41,32 +42,29 @@ public class ClassInterfacesType extends ReferenceType {
ClassInfo ifaces[]; ClassInfo ifaces[];
public ClassInfo getClazz() { public ClassInfo getClazz() {
return clazz != null ? clazz : ClassInfo.javaLangObject; return clazz != null ? clazz
: ClassInfo.forName("java.lang.Object");
} }
public ClassInterfacesType(String clazzName) { public ClassInterfacesType(String clazzName) {
super(TC_CLASS); this(ClassInfo.forName(clazzName));
ClassInfo clazz = ClassInfo.forName(clazzName);
if (clazz.isInterface()) {
this.clazz = null;
ifaces = new ClassInfo[] {clazz};
} else {
this.clazz =
(clazz == ClassInfo.javaLangObject) ? null : clazz;
ifaces = new ClassInfo[0];
}
} }
public ClassInterfacesType(ClassInfo clazz) { public ClassInterfacesType(ClassInfo clazz) {
super(TC_CLASS); super(TC_CLASS);
if (clazz.isInterface()) { try {
this.clazz = null; clazz.load(ClassInfo.HIERARCHY);
ifaces = new ClassInfo[] { clazz }; } catch (IOException ex) {
} else { clazz.guess(ClassInfo.HIERARCHY);
this.clazz = }
(clazz == ClassInfo.javaLangObject) ? null : clazz; if (clazz.isInterface()) {
ifaces = new ClassInfo[0]; this.clazz = null;
} ifaces = new ClassInfo[] { clazz };
} else {
this.clazz =
(clazz.getName() == "java.lang.Object") ? null : clazz;
ifaces = new ClassInfo[0];
}
} }
public ClassInterfacesType(ClassInfo clazz, ClassInfo[] ifaces) { public ClassInterfacesType(ClassInfo clazz, ClassInfo[] ifaces) {
@ -103,9 +101,9 @@ public class ClassInterfacesType extends ReferenceType {
|| (clazz == null && ifaces.length == 1)) || (clazz == null && ifaces.length == 1))
return this; return this;
if (clazz != null) if (clazz != null)
return Type.tClass(clazz.getName()); return Type.tClass(clazz);
else else
return Type.tClass(ifaces[0].getName()); return Type.tClass(ifaces[0]);
} }
public Type getCanonic() { public Type getCanonic() {
@ -113,9 +111,9 @@ public class ClassInterfacesType extends ReferenceType {
|| (clazz == null && ifaces.length == 1)) || (clazz == null && ifaces.length == 1))
return this; return this;
if (clazz != null) if (clazz != null)
return Type.tClass(clazz.getName()); return Type.tClass(clazz);
else else
return Type.tClass(ifaces[0].getName()); return Type.tClass(ifaces[0]);
} }
/** /**
@ -136,6 +134,7 @@ public class ClassInterfacesType extends ReferenceType {
if (bottomType == tObject) if (bottomType == tObject)
return (this == tObject) ? tObject : tRange(tObject, this); return (this == tObject) ? tObject : tRange(tObject, this);
try {
if (bottom.clazz != null) { if (bottom.clazz != null) {
/* The searched type must be a class type. /* The searched type must be a class type.
*/ */
@ -207,6 +206,10 @@ public class ClassInterfacesType extends ReferenceType {
return tRange(bottom, this); return tRange(bottom, this);
return tRange(bottom, create(clazz, ifaces)); return tRange(bottom, create(clazz, ifaces));
} }
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
} }
/** /**
@ -235,6 +238,7 @@ public class ClassInterfacesType extends ReferenceType {
* class of the other or null. * class of the other or null.
*/ */
try {
if (this.clazz == null) if (this.clazz == null)
clazz = other.clazz; clazz = other.clazz;
else if (other.clazz == null) else if (other.clazz == null)
@ -245,6 +249,10 @@ public class ClassInterfacesType extends ReferenceType {
clazz = this.clazz; clazz = this.clazz;
else else
return tError; return tError;
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
/* Most times (99.9999999 %) one of the two classes is already /* Most times (99.9999999 %) one of the two classes is already
* more specialized. Optimize for this case. (I know of one * more specialized. Optimize for this case. (I know of one
@ -258,6 +266,7 @@ public class ClassInterfacesType extends ReferenceType {
this.ifaces)) this.ifaces))
return other; return other;
try {
/* The interfaces are simply the union of both interfaces set. /* The interfaces are simply the union of both interfaces set.
* But we can simplify this, if an interface is implemented by * But we can simplify this, if an interface is implemented by
* another or by the class, we can omit it. * another or by the class, we can omit it.
@ -302,6 +311,10 @@ public class ClassInterfacesType extends ReferenceType {
ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()]; ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()];
ifaces.copyInto(ifaceArray); ifaces.copyInto(ifaceArray);
return create(clazz, ifaceArray); return create(clazz, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
} }
/** /**
@ -311,6 +324,7 @@ public class ClassInterfacesType extends ReferenceType {
* interfaces, that one class or interface of each type * interfaces, that one class or interface of each type
* implements. */ * implements. */
public Type getGeneralizedType(Type type) { public Type getGeneralizedType(Type type) {
try{
int code = type.typecode; int code = type.typecode;
if (code == TC_RANGE) { if (code == TC_RANGE) {
type = ((RangeType) type).getTop(); type = ((RangeType) type).getTop();
@ -331,13 +345,18 @@ public class ClassInterfacesType extends ReferenceType {
else { else {
clazz = this.clazz; clazz = this.clazz;
while(clazz != null) { try {
if (clazz.superClassOf(other.clazz)) while(clazz != null) {
break; if (clazz.superClassOf(other.clazz))
clazz = clazz.getSuperclass(); break;
} clazz = clazz.getSuperclass();
if (clazz == ClassInfo.javaLangObject) }
clazz = null; if (clazz.getName() == "java.lang.Object")
clazz = null;
} catch (IOException ex) {
/*XXX : There is something better than tObject*/
clazz = null;
}
} }
if (clazz == this.clazz if (clazz == this.clazz
@ -404,6 +423,10 @@ public class ClassInterfacesType extends ReferenceType {
ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()]; ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()];
ifaces.copyInto(ifaceArray); ifaces.copyInto(ifaceArray);
return create(clazz, ifaceArray); return create(clazz, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
} }
public String getTypeSignature() { public String getTypeSignature() {
@ -430,7 +453,7 @@ public class ClassInterfacesType extends ReferenceType {
else if (ifaces.length > 0) else if (ifaces.length > 0)
return ifaces[0]; return ifaces[0];
else else
return ClassInfo.javaLangObject; return ClassInfo.forName("java.lang.Object");
} }
public String toString() public String toString()
@ -472,17 +495,22 @@ public class ClassInterfacesType extends ReferenceType {
else else
return tObject; return tObject;
case TC_CLASS: case TC_CLASS:
ClassInterfacesType hint = (ClassInterfacesType) hintType; try {
if (hint.clazz == null || clazz == null ClassInterfacesType hint = (ClassInterfacesType) hintType;
|| clazz.superClassOf(hint.clazz) if (hint.clazz == null || clazz == null
|| hint.clazz.superClassOf(clazz)) || clazz.superClassOf(hint.clazz)
return null; || hint.clazz.superClassOf(clazz))
ClassInfo superClazz = clazz.getSuperclass(); return null;
while (superClazz != null ClassInfo superClazz = clazz.getSuperclass();
&& !superClazz.superClassOf(hint.clazz)) { while (superClazz != null
superClazz = superClazz.getSuperclass(); && !superClazz.superClassOf(hint.clazz)) {
superClazz = superClazz.getSuperclass();
}
return tClass(superClazz.getName());
} catch (IOException ex) {
/*XXX : There is something better than tObject*/
return tObject;
} }
return tClass(superClazz.getName());
case TC_UNKNOWN: case TC_UNKNOWN:
return null; return null;
} }
@ -551,7 +579,7 @@ public class ClassInterfacesType extends ReferenceType {
else if (ifaces.length > 0) else if (ifaces.length > 0)
type = ifaces[0]; type = ifaces[0];
else else
type = ClassInfo.javaLangObject; type = ClassInfo.forName("java.lang.Object");
String name = type.getName(); String name = type.getName();
int dot = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$')); int dot = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$'));
if (dot >= 0) if (dot >= 0)

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@

@ -20,8 +20,7 @@
package jode.type; package jode.type;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import java.util.Vector; import java.io.IOException;
import java.util.Stack;
/** /**
* This is an abstrace super class of all reference types. Reference * This is an abstrace super class of all reference types. Reference
@ -83,26 +82,37 @@ public abstract class ReferenceType extends Type {
* *
* This is a useful function for generalizing/specializing interface * This is a useful function for generalizing/specializing interface
* types or arrays. * types or arrays.
*
* If it can't find all classes in the hierarchy, it will catch this
* error and return false, i.e. it assumes that the class doesn't
* implement all interfaces.
*
* @param clazz The clazz, can be null. * @param clazz The clazz, can be null.
* @param ifaces The ifaces. * @param ifaces The ifaces.
* @param otherifaces The other ifaces, that must be implemented. * @param otherifaces The other ifaces, that must be implemented.
* @return true, if all otherIfaces are implemented. * @return true, if all otherIfaces are implemented, false if unsure or
* if not all otherIfaces are implemented.
*/ */
protected static boolean implementsAllIfaces(ClassInfo clazz, protected static boolean implementsAllIfaces(ClassInfo clazz,
ClassInfo[] ifaces, ClassInfo[] ifaces,
ClassInfo[] otherIfaces) { ClassInfo[] otherIfaces) {
big: try {
for (int i=0; i < otherIfaces.length; i++) { big:
ClassInfo iface = otherIfaces[i]; for (int i=0; i < otherIfaces.length; i++) {
if (clazz != null && iface.implementedBy(clazz)) ClassInfo iface = otherIfaces[i];
continue big; if (clazz != null && iface.implementedBy(clazz))
for (int j=0; j < ifaces.length; j++) { continue big;
if (iface.implementedBy(ifaces[j])) for (int j=0; j < ifaces.length; j++) {
if (iface.implementedBy(ifaces[j]))
continue big; continue big;
} }
return false; return false;
} }
return true; return true;
} catch (IOException ex) {
/* Class Hierarchy can't be fully gotten. */
return false;
}
} }
public Type getSuperType() { public Type getSuperType() {
@ -136,7 +146,7 @@ public abstract class ReferenceType extends Type {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) {
GlobalOptions.err.println("intersecting "+ this +" and "+ type + GlobalOptions.err.println("intersecting "+ this +" and "+ type +
" to " + result); " to " + result);
} }
return result; return result;
} }

@ -23,7 +23,9 @@ import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.util.UnifyHash; import jode.util.UnifyHash;
import @COLLECTIONS@.Iterator; ///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
/** /**
* This is my type class. It differs from java.lang.class, in that it * This is my type class. It differs from java.lang.class, in that it
@ -208,6 +210,7 @@ public class Type {
* @param clazzname the full qualified name of the class. * @param clazzname the full qualified name of the class.
* The packages may be separated by `.' or `/'. * The packages may be separated by `.' or `/'.
* @return a singleton set containing the given type. * @return a singleton set containing the given type.
* @deprecated Use tClass(ClassInfo)
*/ */
public static final ClassInterfacesType tClass(String clazzname) { public static final ClassInterfacesType tClass(String clazzname) {
return tClass(ClassInfo.forName(clazzname.replace('/','.'))); return tClass(ClassInfo.forName(clazzname.replace('/','.')));

@ -1,10 +1,8 @@
## Input file for automake to generate the Makefile.in used by configure ## Input file for automake to generate the Makefile.in used by configure
JAR = @JAR@ JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
JAVAC = @JAVAC@ -subdir=$(subdir) -dependdir=$(top_builddir) \
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
CLASSPATH = @CLASSPATH@ CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@ CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@ SUBSTCP = @SUBSTCP@
@ -14,6 +12,7 @@ MY_JAVA_FILES = \
ArrayEnum.java \ ArrayEnum.java \
SimpleMap.java \ SimpleMap.java \
SimpleSet.java \ SimpleSet.java \
StringQuoter.java \
UnifyHash.java UnifyHash.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class) noinst_DATA = $(MY_JAVA_FILES:.java=.class)

@ -18,10 +18,12 @@
*/ */
package jode.util; package jode.util;
import @COLLECTIONS@.AbstractMap; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Map; import java.util.AbstractMap;
import @COLLECTIONS@.Iterator; import java.util.Map;
import @COLLECTIONS@.Set; import java.util.Iterator;
import java.util.Set;
///#enddef
/** /**
* This is a very simple map, using a set as backing. * This is a very simple map, using a set as backing.

@ -18,8 +18,10 @@
*/ */
package jode.util; package jode.util;
import @COLLECTIONS@.AbstractSet; ///#def COLLECTIONS java.util
import @COLLECTIONS@.Iterator; import java.util.AbstractSet;
import java.util.Iterator;
///#enddef
public class SimpleSet extends AbstractSet implements Cloneable public class SimpleSet extends AbstractSet implements Cloneable
{ {

@ -0,0 +1,82 @@
package jode.util;
/**
* This is a simple class to quote a string or a char. It puts it in
* quotes (" resp. ') and prints special chars with the same syntax as
* strings and chars in java source codes.
*/
public class StringQuoter {
/**
* This is the static method, that quotes a string.
*/
public static String quote(String str) {
StringBuffer result = new StringBuffer("\"");
for (int i=0; i< str.length(); i++) {
char c;
switch (c = str.charAt(i)) {
case '\0':
result.append("\\0");
break;
case '\t':
result.append("\\t");
break;
case '\n':
result.append("\\n");
break;
case '\r':
result.append("\\r");
break;
case '\\':
result.append("\\\\");
break;
case '\"':
result.append("\\\"");
break;
default:
if (c < 32) {
String oct = Integer.toOctalString(c);
result.append("\\000".substring(0, 4-oct.length()))
.append(oct);
} else if (c >= 32 && c < 127)
result.append(str.charAt(i));
else {
String hex = Integer.toHexString(c);
result.append("\\u0000".substring(0, 6-hex.length()))
.append(hex);
}
}
}
return result.append("\"").toString();
}
/**
* This is the static method, that quotes a char.
*/
public static String quote(char c) {
switch (c) {
case '\0':
return "\'\\0\'";
case '\t':
return "\'\\t\'";
case '\n':
return "\'\\n\'";
case '\r':
return "\'\\r\'";
case '\\':
return "\'\\\\\'";
case '\"':
return "\'\\\"\'";
case '\'':
return "\'\\\'\'";
}
if (c < 32) {
String oct = Integer.toOctalString(c);
return "\'\\000".substring(0, 5-oct.length())+oct+"\'";
}
if (c >= 32 && c < 127)
return "\'"+c+"\'";
else {
String hex = Integer.toHexString(c);
return "\'\\u0000".substring(0, 7-hex.length())+hex+"\'";
}
}
}

@ -19,16 +19,20 @@
package jode.util; package jode.util;
///#ifdef JDK12 ///#ifdef JDK12
///import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
///import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
///#endif ///#endif
import @COLLECTIONS@.Comparator; ///#def COLLECTIONS java.util
import @COLLECTIONS@.AbstractCollection; import java.util.Comparator;
import @COLLECTIONS@.Iterator; import java.util.AbstractCollection;
import @COLLECTIONS@.NoSuchElementException; import java.util.Iterator;
import @COLLECTIONS@.ConcurrentModificationException; import java.util.NoSuchElementException;
import @COLLECTIONEXTRA@.UnsupportedOperationException; import java.util.ConcurrentModificationException;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
public class UnifyHash extends AbstractCollection { public class UnifyHash extends AbstractCollection {
/** /**
@ -40,28 +44,28 @@ public class UnifyHash extends AbstractCollection {
private static final float DEFAULT_LOAD_FACTOR = 0.75F; private static final float DEFAULT_LOAD_FACTOR = 0.75F;
///#ifdef JDK12 ///#ifdef JDK12
/// private ReferenceQueue queue = new ReferenceQueue(); private ReferenceQueue queue = new ReferenceQueue();
///#endif ///#endif
static class Bucket static class Bucket
///#ifdef JDK12 ///#ifdef JDK12
/// extends WeakReference extends WeakReference
///#endif ///#endif
{ {
///#ifdef JDK12 ///#ifdef JDK12
/// public Bucket(Object o, ReferenceQueue q) { public Bucket(Object o, ReferenceQueue q) {
/// super(o, q); super(o, q);
/// }
///#else
public Bucket(Object o) {
this.obj = o;
}
Object obj;
public Object get() {
return obj;
} }
///#else
/// public Bucket(Object o) {
/// this.obj = o;
/// }
///
/// Object obj;
///
/// public Object get() {
/// return obj;
/// }
///#endif ///#endif
int hash; int hash;
@ -107,21 +111,21 @@ public class UnifyHash extends AbstractCollection {
} }
///#ifdef JDK12 ///#ifdef JDK12
/// public final void cleanUp() { public final void cleanUp() {
/// Bucket died; Bucket died;
/// while ((died = (Bucket)queue.poll()) != null) { while ((died = (Bucket)queue.poll()) != null) {
/// int diedSlot = Math.abs(died.hash % buckets.length); int diedSlot = Math.abs(died.hash % buckets.length);
/// if (buckets[diedSlot] == died) if (buckets[diedSlot] == died)
/// buckets[diedSlot] = died.next; buckets[diedSlot] = died.next;
/// else { else {
/// Bucket b = buckets[diedSlot]; Bucket b = buckets[diedSlot];
/// while (b.next != died) while (b.next != died)
/// b = b.next; b = b.next;
/// b.next = died.next; b.next = died.next;
/// } }
/// size--; size--;
/// } }
/// } }
///#endif ///#endif
@ -131,7 +135,7 @@ public class UnifyHash extends AbstractCollection {
public Iterator iterator() { public Iterator iterator() {
///#ifdef JDK12 ///#ifdef JDK12
/// cleanUp(); cleanUp();
///#endif ///#endif
return new Iterator() { return new Iterator() {
@ -232,9 +236,9 @@ public class UnifyHash extends AbstractCollection {
int slot = Math.abs(hash % buckets.length); int slot = Math.abs(hash % buckets.length);
///#ifdef JDK12 ///#ifdef JDK12
/// Bucket b = new Bucket(o, queue); Bucket b = new Bucket(o, queue);
///#else ///#else
Bucket b = new Bucket(o); /// Bucket b = new Bucket(o);
///#endif ///#endif
b.hash = hash; b.hash = hash;
b.next = buckets[slot]; b.next = buckets[slot];
@ -243,7 +247,7 @@ public class UnifyHash extends AbstractCollection {
public Object unify(Object o, int hash, Comparator comparator) { public Object unify(Object o, int hash, Comparator comparator) {
///#ifdef JDK12 ///#ifdef JDK12
/// cleanUp(); cleanUp();
///#endif ///#endif
int slot = Math.abs(hash % buckets.length); int slot = Math.abs(hash % buckets.length);
for (Bucket b = buckets[slot]; b != null; b = b.next) { for (Bucket b = buckets[slot]; b != null; b = b.next) {
Loading…
Cancel
Save