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 25 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@
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
-subdir=$(subdir) -dependdir=$(top_builddir) \
-classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@
@ -19,7 +17,6 @@ MY_JAVA_FILES = \
noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES)
JARFILE = jode-@VERSION@.jar
#data_DATA = $(JARFILE)

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

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

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

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

@ -20,18 +20,19 @@
package jode.decompiler;
import jode.GlobalOptions;
import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
import jode.type.Type;
import jode.type.ArrayType;
import jode.type.ClassInterfacesType;
import jode.type.NullType;
import @COLLECTIONS@.SortedMap;
import @COLLECTIONS@.TreeMap;
import @COLLECTIONS@.List;
import @COLLECTIONS@.LinkedList;
import @COLLECTIONS@.Comparator;
import @COLLECTIONS@.Iterator;
///#def COLLECTIONS java.util
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Comparator;
import java.util.Iterator;
///#enddef
import java.io.IOException;
import java.util.Hashtable;
@ -252,17 +253,25 @@ public class ImportHandler {
*/
public void useClass(ClassInfo clazz) {
for (;;) {
/* First handle inner classes: For class scoped classes
* import outer class instead; for method scoped classes
* we don't import anything.
*/
InnerClassInfo[] outerInfo = clazz.getOuterClasses();
if (outerInfo == null)
try {
/* First handle inner classes: For class scoped classes
* import outer class instead; for method scoped classes
* we don't import anything.
*/
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;
if (outerInfo[0].name == null || outerInfo[0].outer == null)
}
if (clazz.isMethodScoped())
return;
clazz = ClassInfo.forName(outerInfo[0].outer);
ClassInfo outer = clazz.getOuterClass();
if (outer == null)
break;
clazz = outer;
}
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;
import jode.bytecode.ClassInfo;
import jode.bytecode.SearchPath;
import jode.bytecode.ClassPath;
import jode.GlobalOptions;
import java.io.BufferedOutputStream;
@ -144,8 +144,9 @@ public class Main extends Options {
return;
}
String classPath = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, SearchPath.altPathSeparatorChar);
ClassPath classPath;
String classPathStr = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, ClassPath.altPathSeparatorChar);
String destDir = null;
int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
@ -168,7 +169,7 @@ public class Main extends Options {
GlobalOptions.err.println(GlobalOptions.version);
break;
case 'c':
classPath = g.getOptarg();
classPathStr = g.getOptarg();
break;
case 'd':
destDir = g.getOptarg();
@ -246,7 +247,8 @@ public class Main extends Options {
}
if (errorInParams)
return;
ClassInfo.setClassPath(classPath.toString());
classPath = new ClassPath(classPathStr);
ClassInfo.setClassPath(classPath);
ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit);
@ -270,7 +272,7 @@ public class Main extends Options {
try {
ClassInfo clazz;
try {
clazz = ClassInfo.forName(params[i]);
clazz = classPath.getClassInfo(params[i]);
} catch (IllegalArgumentException ex) {
GlobalOptions.err.println
("`"+params[i]+"' is not a class name");

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

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

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

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

@ -18,13 +18,6 @@
*/
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

@ -37,19 +37,24 @@ public interface Scope {
public final int NOSUPERMETHODNAME = 12;
public final int NOSUPERFIELDNAME = 13;
/**
* Tells that we want to allow a classanalyzer as scope.
*/
public final int CLASSSCOPE = 1;
public final int METHODSCOPE = 2;
/**
* Simplifies the given name.
* @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.
* Tells that we want to allow a methodanalyzer as scope.
*/
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 conflicts(String name, int usageType);
}

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

@ -22,7 +22,9 @@ import jode.type.Type;
import jode.decompiler.LocalInfo;
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

@ -20,6 +20,7 @@
package jode.expr;
import jode.type.Type;
import jode.type.IntegerType;
import jode.util.StringQuoter;
import jode.decompiler.TabbedPrintWriter;
public class ConstOperator extends NoArgOperator {
@ -113,46 +114,6 @@ public class ConstOperator extends NoArgOperator {
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() {
String strVal = String.valueOf(value);
if (type.isOfType(Type.tBoolean)) {
@ -167,34 +128,9 @@ public class ConstOperator extends NoArgOperator {
}
if (type.getHint().equals(Type.tChar)) {
char c = (char) ((Integer) value).intValue();
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+"\'";
}
return StringQuoter.quote(c);
} else if (type.equals(Type.tString)) {
return quoted(strVal);
return StringQuoter.quote(strVal);
} else if (parent != null) {
int opindex = parent.getOperatorIndex();
if (opindex >= OPASSIGN_OP + ADD_OP

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

@ -18,13 +18,13 @@
*/
package jode.expr;
import jode.GlobalOptions;
import jode.type.Type;
import jode.type.NullType;
import jode.type.ClassInterfacesType;
import jode.bytecode.FieldInfo;
import jode.bytecode.ClassInfo;
import jode.bytecode.Reference;
import jode.bytecode.InnerClassInfo;
import jode.decompiler.MethodAnalyzer;
import jode.decompiler.ClassAnalyzer;
import jode.decompiler.MethodAnalyzer;
@ -33,7 +33,10 @@ import jode.decompiler.Options;
import jode.decompiler.TabbedPrintWriter;
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
@ -44,6 +47,8 @@ public abstract class FieldOperator extends Operator {
boolean staticFlag;
Reference ref;
Type classType;
ClassInfo classInfo;
String callerPackage;
public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag,
Reference ref) {
@ -55,6 +60,21 @@ public abstract class FieldOperator extends Operator {
if (staticFlag)
methodAnalyzer.useType(classType);
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() {
@ -75,9 +95,7 @@ public abstract class FieldOperator extends Operator {
}
public ClassInfo getClassInfo() {
if (classType instanceof ClassInterfacesType)
return ((ClassInterfacesType) classType).getClassInfo();
return null;
return classInfo;
}
/**
@ -87,7 +105,7 @@ public abstract class FieldOperator extends Operator {
* @return see above.
*/
public FieldAnalyzer getField() {
ClassInfo clazz = getClassInfo();
ClassInfo clazz = classInfo;
if (clazz != null) {
ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
while (true) {
@ -120,6 +138,21 @@ public abstract class FieldOperator extends Operator {
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) {
if (type instanceof NullType)
return true;
@ -140,25 +173,16 @@ public abstract class FieldOperator extends Operator {
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.
*/
public void fillDeclarables(Collection used) {
ClassInfo clazz = getClassInfo();
InnerClassInfo outer = getOuterClassInfo(clazz);
ClassAnalyzer clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
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.getParent() == methodAnalyzer) {

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

@ -23,7 +23,9 @@ import jode.type.Type;
import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection;
///#def COLLECTIONS java.util
import java.util.Collection;
///#enddef
public abstract class LocalVarOperator extends Operator {
LocalInfo local;
@ -49,6 +51,14 @@ public abstract class LocalVarOperator extends Operator {
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) {
used.add(local);
super.fillDeclarables(used);

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

@ -22,8 +22,10 @@ import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter;
import @COLLECTIONS@.Collection;
import @COLLECTIONS@.Set;
///#def COLLECTIONS java.util
import java.util.Collection;
import java.util.Set;
///#enddef
public abstract class Operator extends Expression {
/* 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) {
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++)
subExpressions[i].fillInGenSet(in,gen);
}

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

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

@ -36,23 +36,10 @@ public class ConditionalBlock extends InstructionContainer {
public void checkConsistent() {
super.checkConsistent();
if (trueBlock.jump == null
|| !(trueBlock instanceof EmptyBlock))
if (!(trueBlock instanceof EmptyBlock))
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.
*/
@ -69,6 +56,19 @@ public class ConditionalBlock extends InstructionContainer {
* 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.
*/

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

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

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

@ -24,7 +24,9 @@ import jode.expr.Expression;
import jode.type.Type;
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
@ -90,7 +92,7 @@ public class IfThenElseBlock extends StructuredBlock {
* @return false, if oldBlock wasn't a direct sub block.
*/
public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) {
StructuredBlock newBlock) {
if (thenBlock == oldBlock)
thenBlock = newBlock;
else if (elseBlock == oldBlock)

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

@ -24,7 +24,9 @@ import jode.expr.InvokeOperator;
import jode.expr.LocalVarOperator;
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.
@ -39,11 +41,6 @@ public abstract class InstructionContainer extends StructuredBlock {
this.instr = instr;
}
public InstructionContainer(Expression instr, Jump jump) {
this(instr);
setJump(jump);
}
/**
* Make the declarations, i.e. initialize the declare variable
* to correct values. This will declare every variable that

@ -34,13 +34,24 @@ public class JsrBlock extends StructuredBlock {
*/
StructuredBlock innerBlock;
public JsrBlock(Jump subroutine, Jump next) {
innerBlock = new EmptyBlock(subroutine);
public JsrBlock() {
innerBlock = new EmptyBlock();
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
* implementation */

@ -28,7 +28,9 @@ import jode.expr.LocalStoreOperator;
import jode.expr.CombineableOperator;
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.
@ -211,6 +213,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
bodyBlock = newBlock;
else
return false;
newBlock.outer = this;
oldBlock.outer = null;
return true;
}

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

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

@ -35,7 +35,11 @@ public class ReturnBlock extends InstructionContainer {
}
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.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
@ -219,7 +221,7 @@ public class SequentialBlock extends StructuredBlock {
* @return false, if oldBlock wasn't a direct sub block.
*/
public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) {
StructuredBlock newBlock) {
for (int i=0; i<2; i++) {
if (subBlocks[i] == oldBlock) {
subBlocks[i] = newBlock;

@ -21,9 +21,12 @@ package jode.flow;
import jode.decompiler.LocalInfo;
import jode.util.ArrayEnum;
import @COLLECTIONS@.Collection;
import @COLLECTIONS@.AbstractSet;
import @COLLECTIONS@.Iterator;
///#def COLLECTIONS java.util
import java.util.Collection;
import java.util.AbstractSet;
import java.util.Set;
import java.util.Iterator;
///#enddef
/**
* 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];
int slot = li.getSlot();
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]);
}
}
@ -190,7 +193,19 @@ public final class SlotSet extends AbstractSet implements Cloneable {
*
* @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());
big_loop:
for (Iterator i = kill.iterator(); i.hasNext(); ) {

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

@ -26,9 +26,11 @@ import jode.decompiler.Declarable;
import jode.decompiler.ClassAnalyzer;
import jode.util.SimpleSet;
import @COLLECTIONS@.Collections;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.Set;
///#def COLLECTIONS java.util
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
///#enddef
/**
* A structured block is the building block of the source programm.
@ -118,6 +120,18 @@ public abstract class StructuredBlock {
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) {
this.jump = jump;
jump.prev = this;
@ -199,7 +213,8 @@ public abstract class StructuredBlock {
/**
* 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() {
if (jump != null) {
jump.prev = null;
@ -560,21 +575,9 @@ public abstract class StructuredBlock {
* @param in The VariableSet, the in variables should be stored to.
*/
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();
for (int i=0; i<subs.length; i++) {
subs[i].fillSuccessors();
}
for (int i=0; i<subs.length; i++)
subs[i].fillInGenSet(in, gen);
}
/**

@ -21,6 +21,11 @@ package jode.flow;
import jode.decompiler.TabbedPrintWriter;
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.
*/
@ -30,55 +35,81 @@ implements BreakableBlock {
VariableStack exprStack;
VariableStack breakedStack;
public SwitchBlock(Expression instr,
int[] cases, FlowBlock[] dests) {
public SwitchBlock(Expression instr, int[] cases) {
super(instr);
/* First remove all dests that jump to the default dest. */
int numCases = dests.length;
FlowBlock defaultDest = dests[cases.length];
this.caseBlocks = new CaseBlock[cases.length + 1];
for (int i=0; i< cases.length; i++) {
if (dests[i] == defaultDest) {
dests[i] = null;
numCases--;
}
caseBlocks[i] = new CaseBlock(cases[i]);
caseBlocks[i].outer = this;
}
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];
FlowBlock lastDest = null;
for (int i=numCases-1; i>=0; i--) {
/**
* Sort the destinations by finding the greatest destAddr
*/
int index = 0;
for (int j=1; j<dests.length; j++) {
if (dests[j] != null
&& (dests[index] == null
|| dests[j].getAddr() >= dests[index].getAddr()))
index = j;
public boolean doTransformations() {
return super.doTransformations();
}
public void doJumpTrafo() {
/* First remember the default destination */
FlowBlock defaultDest
= caseBlocks[caseBlocks.length-1].subBlock.jump.destination;
Comparator caseBlockComparator = new Comparator() {
public int compare(Object o1, Object o2) {
CaseBlock c1 = (CaseBlock) o1;
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) */
int value;
if (index == cases.length)
value = -1;
else
value = cases[index];
if (dests[index] == lastDest)
caseBlocks[i] = new CaseBlock(value);
else
caseBlocks[i] = new CaseBlock(value,
new Jump(dests[index]));
caseBlocks[i].outer = this;
lastDest = dests[index];
dests[index] = null;
if (index == cases.length)
caseBlocks[i].isDefault = true;
}
caseBlocks[numCases-1].isLastBlock = true;
this.jump = null;
isBreaked = false;
};
Arrays.sort(caseBlocks, caseBlockComparator);
int newCases = 0;
for (int i=0; i < caseBlocks.length; i++) {
Jump jump = caseBlocks[i].subBlock.jump;
if (i < caseBlocks.length - 1
&& jump.destination
== caseBlocks[i+1].subBlock.jump.destination) {
// This case falls into the next one.
caseBlocks[i].subBlock.removeJump();
flowBlock.removeSuccessor(jump);
if (caseBlocks[i+1].subBlock.jump.destination == defaultDest)
continue; // remove this case, it jumps to the default.
}
caseBlocks[newCases++] = caseBlocks[i];
}
caseBlocks[newCases-1].isLastBlock = true;
CaseBlock[] newCaseBlocks = new CaseBlock[newCases];
System.arraycopy(caseBlocks, 0, newCaseBlocks, 0, newCases);
caseBlocks = newCaseBlocks;
}
/**
@ -195,7 +226,7 @@ implements BreakableBlock {
writer.tab();
}
writer.print("switch (");
instr.dumpExpression(writer.EXPL_PAREN, writer);
instr.dumpExpression(writer);
writer.print(")");
writer.openBrace();
for (int i=0; i < caseBlocks.length; i++)

@ -23,7 +23,9 @@ import jode.decompiler.TabbedPrintWriter;
import jode.expr.Expression;
import jode.util.SimpleSet;
import @COLLECTIONS@.Set;
///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/**
* This class represents a synchronized structured block.
@ -106,6 +108,15 @@ public class SynchronizedBlock extends StructuredBlock {
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() {
StructuredBlock last = flowBlock.lastModified;

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

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

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

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

@ -21,17 +21,21 @@ package jode.obfuscator;
import jode.GlobalOptions;
import jode.bytecode.*;
import jode.obfuscator.modules.ModifierMatcher;
import @COLLECTIONS@.Comparator;
import @COLLECTIONS@.Collection;
import @COLLECTIONS@.Collections;
import @COLLECTIONS@.ArrayList;
import @COLLECTIONS@.Arrays;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.List;
import @COLLECTIONS@.LinkedList;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.Random;
import @COLLECTIONEXTRA@.UnsupportedOperationException;
///#def COLLECTIONS java.util
import java.util.Comparator;
import java.util.Collection;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
@ -52,6 +56,8 @@ public class ClassIdentifier extends Identifier {
List knownSubClasses = new LinkedList();
List virtualReachables = new LinkedList();
boolean initialized;
public ClassIdentifier(PackageIdentifier pack, String fullName,
String name, ClassInfo info) {
super(name);
@ -265,8 +271,12 @@ public class ClassIdentifier extends Identifier {
}
public boolean isSerializable() {
return ClassInfo.forName("java.lang.Serializable")
.implementedBy(info);
try {
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() {
return (findField("serialVersionUID", "J") != null);
@ -331,6 +341,7 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = Main.getClassBundle()
.getClassIdentifier(superclass.getName());
if (superident != null) {
superident.initClass();
for (Iterator i = superident.getMethodIdents().iterator();
i.hasNext(); ) {
MethodIdentifier mid = (MethodIdentifier) i.next();
@ -369,7 +380,16 @@ public class ClassIdentifier extends Identifier {
}
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();
MethodInfo[] minfos = info.getMethods();
@ -401,15 +421,11 @@ public class ClassIdentifier extends Identifier {
ifaceNames = new String[ifaces.length];
for (int i=0; i < ifaces.length; i++) {
ifaceNames[i] = ifaces[i].getName();
ClassIdentifier ifaceident = Main.getClassBundle()
.getClassIdentifier(ifaceNames[i]);
initSuperClasses(ifaces[i]);
}
if (info.getSuperclass() != null) {
superName = info.getSuperclass().getName();
ClassIdentifier superident = Main.getClassBundle()
.getClassIdentifier(superName);
initSuperClasses(info.getSuperclass());
}
@ -417,35 +433,27 @@ public class ClassIdentifier extends Identifier {
info.setSourceFile(null);
}
if ((Main.stripping & Main.STRIP_INNERINFO) != 0) {
info.setInnerClasses(new InnerClassInfo[0]);
info.setOuterClasses(new InnerClassInfo[0]);
info.setExtraClasses(new InnerClassInfo[0]);
info.setClasses(null);
info.setOuterClass(null);
info.setExtraClasses(null);
}
// load inner classes
InnerClassInfo[] innerClasses = info.getInnerClasses();
InnerClassInfo[] outerClasses = info.getOuterClasses();
InnerClassInfo[] extraClasses = info.getExtraClasses();
if (outerClasses != null) {
for (int i=0; i < outerClasses.length; i++) {
if (outerClasses[i].outer != null) {
Main.getClassBundle()
.getClassIdentifier(outerClasses[i].outer);
}
}
}
ClassInfo outerClass = info.getOuterClass();
ClassInfo[] innerClasses = info.getClasses();
ClassInfo[] extraClasses = info.getExtraClasses();
if (outerClass != null)
Main.getClassBundle().getClassIdentifier(outerClass.getName());
if (innerClasses != null) {
for (int i=0; i < innerClasses.length; i++) {
Main.getClassBundle()
.getClassIdentifier(innerClasses[i].inner);
.getClassIdentifier(innerClasses[i].getName());
}
}
if (extraClasses != null) {
for (int i=0; i < extraClasses.length; i++) {
Main.getClassBundle()
.getClassIdentifier(extraClasses[i].inner);
if (extraClasses[i].outer != null)
Main.getClassBundle()
.getClassIdentifier(extraClasses[i].outer);
.getClassIdentifier(extraClasses[i].getName());
}
}
}
@ -499,142 +507,66 @@ public class ClassIdentifier extends Identifier {
}
public void transformInnerClasses() {
InnerClassInfo[] outerClasses = info.getOuterClasses();
if (outerClasses != null) {
int newOuterCount = outerClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < outerClasses.length; i++) {
if (outerClasses[i].outer != null) {
ClassIdentifier outerIdent = Main.getClassBundle()
.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);
ClassInfo outerClass = info.getOuterClass();
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
while (outerClass != null) {
ClassIdentifier outerIdent = Main.getClassBundle()
.getClassIdentifier(outerClass.getName());
if (outerIdent != null && outerIdent.isReachable())
break;
}
}
info.setOuterClass(outerClass);
InnerClassInfo[] innerClasses = info.getInnerClasses();
ClassInfo[] innerClasses = info.getClasses();
if (innerClasses != null) {
int newInnerCount = innerClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < innerClasses.length; i++) {
ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(innerClasses[i].inner);
if (innerIdent != null && !innerIdent.isReachable())
.getClassIdentifier(innerClasses[i].getName());
if (innerIdent != null && !innerIdent.isReachable()) {
innerClasses[i] = null;
newInnerCount--;
}
}
}
if (newInnerCount == 0) {
info.setInnerClasses(null);
info.setClasses(null);
} else {
InnerClassInfo[] newInners = new InnerClassInfo[newInnerCount];
ClassInfo[] newInners = new ClassInfo[newInnerCount];
int pos = 0;
for (int i=0; i<innerClasses.length; i++) {
ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(innerClasses[i].inner);
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);
if (innerClasses[i] != null)
newInners[pos++] = innerClasses[i];
}
info.setInnerClasses(newInners);
info.setClasses(newInners);
}
}
InnerClassInfo[] extraClasses = info.getExtraClasses();
ClassInfo[] extraClasses = info.getExtraClasses();
if (extraClasses != null) {
int newExtraCount = extraClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < extraClasses.length; i++) {
ClassIdentifier outerIdent = extraClasses[i].outer != null
? (Main.getClassBundle()
.getClassIdentifier(extraClasses[i].outer))
: null;
ClassIdentifier innerIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].inner);
if ((outerIdent != null && !outerIdent.isReachable())
|| (innerIdent != null && !innerIdent.isReachable()))
ClassIdentifier extraIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].getName());
if (extraIdent != null && !extraIdent.isReachable()) {
extraClasses[i] = null;
newExtraCount--;
}
}
}
if (newExtraCount == 0) {
info.setExtraClasses(null);
} else {
InnerClassInfo[] newExtras = newExtraCount > 0
? new InnerClassInfo[newExtraCount] : null;
ClassInfo[] newExtras = new ClassInfo[newExtraCount];
int pos = 0;
for (int i=0; i<extraClasses.length; i++) {
ClassIdentifier outerIdent = extraClasses[i].outer != null
? (Main.getClassBundle()
.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);
if (extraClasses[i] != null)
newExtras[pos++] = extraClasses[i];
}
info.setExtraClasses(newExtras);
}
@ -657,6 +589,8 @@ public class ClassIdentifier extends Identifier {
|| ident.isReachable()) {
ident.doTransformations();
newFields.add(ident.info);
} else if (GlobalOptions.verboseLevel > 2) {
GlobalOptions.err.println("Field "+ ident+" not reachable");
}
}
for (Iterator i = methodIdents.iterator(); i.hasNext(); ) {
@ -665,6 +599,8 @@ public class ClassIdentifier extends Identifier {
|| ident.isReachable()) {
ident.doTransformations();
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
* reference of every getfield/putfield opcode points to the
* 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;
if (containsFieldAliasDirectly(newAlias, typeSig, mm))

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

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

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

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

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

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

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

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

@ -29,14 +29,16 @@ import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.HashMap;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.List;
import @COLLECTIONS@.ArrayList;
import @COLLECTIONS@.Random;
import @COLLECTIONS@.Arrays;
import @COLLECTIONS@.Collections;
///#def COLLECTIONS java.util
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
import java.util.Arrays;
import java.util.Collections;
///#enddef
public class PackageIdentifier extends Identifier {
ClassBundle bundle;
@ -45,6 +47,7 @@ public class PackageIdentifier extends Identifier {
String fullName;
boolean loadOnDemand;
boolean initialized = false;
Map loadedClasses;
List swappedClasses;
Random rand = new Random();
@ -93,7 +96,8 @@ public class PackageIdentifier extends Identifier {
ident.setLoadOnDemand();
} else {
ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz, ClassInfo.forName(subFull));
(this, subFull, subclazz,
bundle.getClassPath().getClassInfo(subFull));
if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("preloading Class "
@ -101,7 +105,6 @@ public class PackageIdentifier extends Identifier {
loadedClasses.put(subclazz, ident);
swappedClasses = null;
bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass();
}
}
// 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) {
String component = matcher.getNextComponent(this);
if (component != null) {
@ -201,12 +132,14 @@ public class PackageIdentifier extends Identifier {
if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("loading Class " +subFull);
ident = new ClassIdentifier(this, subFull, component,
ClassInfo.forName(subFull));
bundle.getClassPath()
.getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) {
loadedClasses.put(component, ident);
if (initialized)
((ClassIdentifier) ident).initClass();
swappedClasses = null;
bundle.addClassIdentifier(ident);
((ClassIdentifier) ident).initClass();
}
} else {
GlobalOptions.err.println
@ -250,7 +183,7 @@ public class PackageIdentifier extends Identifier {
} else {
ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz,
ClassInfo.forName(subFull));
bundle.getClassPath().getClassInfo(subFull));
if (loadOnDemand || matcher.matches(ident)) {
if (GlobalOptions.verboseLevel > 1)
@ -259,7 +192,8 @@ public class PackageIdentifier extends Identifier {
loadedClasses.put(subclazz, ident);
swappedClasses = null;
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) {
if (loadOnDemand)
loadMatchingClasses(preserveRule);

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

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

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

@ -30,14 +30,16 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.BitSet;
import @COLLECTIONS@.Arrays;
import @COLLECTIONS@.Collection;
import @COLLECTIONS@.HashSet;
import @COLLECTIONS@.Set;
import @COLLECTIONS@.HashMap;
import @COLLECTIONS@.Map;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.ListIterator;
///#def COLLECTIONS java.util
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.ListIterator;
///#enddef
/**
* 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(),
* but that can be ignored).
*/
clazz = ClassInfo.javaLangObject;
clazz = ClassInfo.forName("java.lang.Object");
} else {
clazz = ClassInfo.forName
(clName.substring(1, clName.length()-1)

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

@ -24,8 +24,10 @@ import jode.obfuscator.*;
import jode.AssertError;
import jode.GlobalOptions;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.ListIterator;
///#def COLLECTIONS java.util
import java.util.Iterator;
import java.util.ListIterator;
///#enddef
/**
* 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
JAR = @JAR@
JAVAC = @JAVAC@
JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\
-dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \
-depfile=Makefile.dep
JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \
-subdir=$(subdir) -dependdir=$(top_builddir) \
-classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep
CLASSPATH = @CLASSPATH@
CLASSLIB = @CLASSLIB@
SUBSTCP = @SUBSTCP@

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

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

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

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

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

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

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

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

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

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

@ -19,12 +19,15 @@
package jode.swingui;
import jode.GlobalOptions;
import jode.bytecode.ClassPath;
import jode.decompiler.Decompiler;
import jode.decompiler.ProgressListener;
import @JAVAX_SWING@.*;
import @JAVAX_SWING@.event.*;
import @JAVAX_SWING@.tree.*;
///#def JAVAX_SWING javax.swing
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
///#enddef
import java.awt.*;
import java.awt.event.*;
@ -41,6 +44,7 @@ public class Main
JTextArea sourcecodeArea, errorArea;
Thread decompileThread;
String currentClassPath, lastClassName;
ClassPath classPath;
JProgressBar progressBar;
@ -281,12 +285,17 @@ public class Main
frame.setJMenuBar(bar);
}
public ClassPath getClassPath() {
return classPath;
}
ClassPath reflectClassPath = new ClassPath("reflection:");
public void setClassPath(String classpath) {
if (classpath == null || classpath.length() == 0)
classpath = ".";
currentClassPath = classpath;
jode.bytecode.ClassInfo.setClassPath(classpath);
decompiler.setClassPath(classpath);
classPath = new ClassPath(classpath, reflectClassPath);
decompiler.setClassPath(classPath);
if (classTree != null)
classTree.clearSelection();
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) {
String cp = System.getProperty("java.class.path", "");
cp = cp.replace(File.pathSeparatorChar,
Decompiler.altPathSeparatorChar);
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];
else
else {
usage();
return;
}
}
Main win = new Main(cp);
win.show();

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

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

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

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

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

@ -20,8 +20,7 @@
package jode.type;
import jode.GlobalOptions;
import jode.bytecode.ClassInfo;
import java.util.Vector;
import java.util.Stack;
import java.io.IOException;
/**
* 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
* 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 ifaces The ifaces.
* @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,
ClassInfo[] ifaces,
ClassInfo[] otherIfaces) {
big:
for (int i=0; i < otherIfaces.length; i++) {
ClassInfo iface = otherIfaces[i];
if (clazz != null && iface.implementedBy(clazz))
continue big;
for (int j=0; j < ifaces.length; j++) {
if (iface.implementedBy(ifaces[j]))
try {
big:
for (int i=0; i < otherIfaces.length; i++) {
ClassInfo iface = otherIfaces[i];
if (clazz != null && iface.implementedBy(clazz))
continue big;
for (int j=0; j < ifaces.length; j++) {
if (iface.implementedBy(ifaces[j]))
continue big;
}
return false;
}
return true;
}
return false;
}
return true;
} catch (IOException ex) {
/* Class Hierarchy can't be fully gotten. */
return false;
}
}
public Type getSuperType() {
@ -136,7 +146,7 @@ public abstract class ReferenceType extends Type {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) {
GlobalOptions.err.println("intersecting "+ this +" and "+ type +
" to " + result);
" to " + result);
}
return result;
}

@ -23,7 +23,9 @@ import jode.GlobalOptions;
import jode.bytecode.ClassInfo;
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
@ -208,6 +210,7 @@ public class Type {
* @param clazzname the full qualified name of the class.
* The packages may be separated by `.' or `/'.
* @return a singleton set containing the given type.
* @deprecated Use tClass(ClassInfo)
*/
public static final ClassInterfacesType tClass(String clazzname) {
return tClass(ClassInfo.forName(clazzname.replace('/','.')));

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

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

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