diff --git a/jode/jode/GlobalOptions.java.in b/jode/jode/GlobalOptions.java similarity index 100% rename from jode/jode/GlobalOptions.java.in rename to jode/jode/GlobalOptions.java diff --git a/jode/jode/Makefile.am b/jode/jode/Makefile.am index 0dcb7f8..a3a09da 100644 --- a/jode/jode/Makefile.am +++ b/jode/jode/Makefile.am @@ -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) diff --git a/jode/jode/decompiler/ClassAnalyzer.java.in b/jode/jode/decompiler/ClassAnalyzer.java similarity index 88% rename from jode/jode/decompiler/ClassAnalyzer.java.in rename to jode/jode/decompiler/ClassAnalyzer.java index 298bdfa..d1c7c19 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java.in +++ b/jode/jode/decompiler/ClassAnalyzer.java @@ -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; diff --git a/jode/jode/decompiler/DeadCodeAnalysis.java.in b/jode/jode/decompiler/DeadCodeAnalysis.java similarity index 98% rename from jode/jode/decompiler/DeadCodeAnalysis.java.in rename to jode/jode/decompiler/DeadCodeAnalysis.java index 1b5cefb..f524895 100644 --- a/jode/jode/decompiler/DeadCodeAnalysis.java.in +++ b/jode/jode/decompiler/DeadCodeAnalysis.java @@ -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 { diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java index eb8c3c9..a59ac7c 100644 --- a/jode/jode/decompiler/Decompiler.java +++ b/jode/jode/decompiler/Decompiler.java @@ -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 = diff --git a/jode/jode/decompiler/FieldAnalyzer.java.in b/jode/jode/decompiler/FieldAnalyzer.java similarity index 98% rename from jode/jode/decompiler/FieldAnalyzer.java.in rename to jode/jode/decompiler/FieldAnalyzer.java index 449a3cc..3db5c48 100644 --- a/jode/jode/decompiler/FieldAnalyzer.java.in +++ b/jode/jode/decompiler/FieldAnalyzer.java @@ -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(";"); } diff --git a/jode/jode/decompiler/ImportHandler.java.in b/jode/jode/decompiler/ImportHandler.java similarity index 93% rename from jode/jode/decompiler/ImportHandler.java.in rename to jode/jode/decompiler/ImportHandler.java index 94f965f..336a7eb 100644 --- a/jode/jode/decompiler/ImportHandler.java.in +++ b/jode/jode/decompiler/ImportHandler.java @@ -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(); diff --git a/jode/jode/decompiler/LocalVarEntry.java b/jode/jode/decompiler/LocalVarEntry.java deleted file mode 100644 index df53433..0000000 --- a/jode/jode/decompiler/LocalVarEntry.java +++ /dev/null @@ -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; - } -} diff --git a/jode/jode/decompiler/LocalVariableRangeList.java b/jode/jode/decompiler/LocalVariableRangeList.java deleted file mode 100644 index d0dd234..0000000 --- a/jode/jode/decompiler/LocalVariableRangeList.java +++ /dev/null @@ -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); - } -} diff --git a/jode/jode/decompiler/LocalVariableTable.java b/jode/jode/decompiler/LocalVariableTable.java deleted file mode 100644 index 162c5e4..0000000 --- a/jode/jode/decompiler/LocalVariableTable.java +++ /dev/null @@ -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; ithis 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("") || methodName.equals(""); - 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 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); diff --git a/jode/jode/decompiler/Opcodes.java b/jode/jode/decompiler/Opcodes.java index 53cfccb..31e9f2c 100644 --- a/jode/jode/decompiler/Opcodes.java +++ b/jode/jode/decompiler/Opcodes.java @@ -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); } } } + diff --git a/jode/jode/decompiler/Options.java b/jode/jode/decompiler/Options.java index a083181..7db7694 100644 --- a/jode/jode/decompiler/Options.java +++ b/jode/jode/decompiler/Options.java @@ -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()); } } diff --git a/jode/jode/decompiler/ProgressListener.java b/jode/jode/decompiler/ProgressListener.java index c1f08f1..4b1b092 100644 --- a/jode/jode/decompiler/ProgressListener.java +++ b/jode/jode/decompiler/ProgressListener.java @@ -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 diff --git a/jode/jode/decompiler/Scope.java b/jode/jode/decompiler/Scope.java index 600ff71..aaf5beb 100644 --- a/jode/jode/decompiler/Scope.java +++ b/jode/jode/decompiler/Scope.java @@ -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); } + + diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java index b8df179..c6be8c6 100644 --- a/jode/jode/decompiler/TabbedPrintWriter.java +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -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; diff --git a/jode/jode/expr/CheckNullOperator.java.in b/jode/jode/expr/CheckNullOperator.java similarity index 97% rename from jode/jode/expr/CheckNullOperator.java.in rename to jode/jode/expr/CheckNullOperator.java index 3f9735f..d42c38a 100644 --- a/jode/jode/expr/CheckNullOperator.java.in +++ b/jode/jode/expr/CheckNullOperator.java @@ -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 diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java index 9cfcb9b..bcc0561 100644 --- a/jode/jode/expr/ConstOperator.java +++ b/jode/jode/expr/ConstOperator.java @@ -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 diff --git a/jode/jode/expr/Expression.java.in b/jode/jode/expr/Expression.java similarity index 99% rename from jode/jode/expr/Expression.java.in rename to jode/jode/expr/Expression.java index 8e2cd4f..28c48a7 100644 --- a/jode/jode/expr/Expression.java.in +++ b/jode/jode/expr/Expression.java @@ -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; diff --git a/jode/jode/expr/FieldOperator.java.in b/jode/jode/expr/FieldOperator.java similarity index 83% rename from jode/jode/expr/FieldOperator.java.in rename to jode/jode/expr/FieldOperator.java index 951f874..0f0de28 100644 --- a/jode/jode/expr/FieldOperator.java.in +++ b/jode/jode/expr/FieldOperator.java @@ -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) { diff --git a/jode/jode/expr/InvokeOperator.java.in b/jode/jode/expr/InvokeOperator.java similarity index 81% rename from jode/jode/expr/InvokeOperator.java.in rename to jode/jode/expr/InvokeOperator.java index 5c6b131..e3b4662 100644 --- a/jode/jode/expr/InvokeOperator.java.in +++ b/jode/jode/expr/InvokeOperator.java @@ -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(); diff --git a/jode/jode/expr/LocalVarOperator.java.in b/jode/jode/expr/LocalVarOperator.java similarity index 87% rename from jode/jode/expr/LocalVarOperator.java.in rename to jode/jode/expr/LocalVarOperator.java index f282e4a..1f8b3e5 100644 --- a/jode/jode/expr/LocalVarOperator.java.in +++ b/jode/jode/expr/LocalVarOperator.java @@ -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); diff --git a/jode/jode/expr/Makefile.am b/jode/jode/expr/Makefile.am index 93dc60a..82cae20 100644 --- a/jode/jode/expr/Makefile.am +++ b/jode/jode/expr/Makefile.am @@ -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@ diff --git a/jode/jode/expr/Operator.java.in b/jode/jode/expr/Operator.java similarity index 97% rename from jode/jode/expr/Operator.java.in rename to jode/jode/expr/Operator.java index 3d77e90..f1c5cee 100644 --- a/jode/jode/expr/Operator.java.in +++ b/jode/jode/expr/Operator.java @@ -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); } diff --git a/jode/jode/flow/CaseBlock.java b/jode/jode/flow/CaseBlock.java index b176261..ab5701b 100644 --- a/jode/jode/flow/CaseBlock.java +++ b/jode/jode/flow/CaseBlock.java @@ -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(); + } } /** diff --git a/jode/jode/flow/CatchBlock.java.in b/jode/jode/flow/CatchBlock.java similarity index 98% rename from jode/jode/flow/CatchBlock.java.in rename to jode/jode/flow/CatchBlock.java index bc24007..34d1898 100644 --- a/jode/jode/flow/CatchBlock.java.in +++ b/jode/jode/flow/CatchBlock.java @@ -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 /** diff --git a/jode/jode/flow/ConditionalBlock.java b/jode/jode/flow/ConditionalBlock.java index d7d007b..a223d3d 100644 --- a/jode/jode/flow/ConditionalBlock.java +++ b/jode/jode/flow/ConditionalBlock.java @@ -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. */ diff --git a/jode/jode/flow/CreateCheckNull.java b/jode/jode/flow/CreateCheckNull.java index 7729fe3..e93aaf1 100644 --- a/jode/jode/flow/CreateCheckNull.java +++ b/jode/jode/flow/CreateCheckNull.java @@ -82,7 +82,6 @@ public class CreateCheckNull { * DUP * if (POP == null) { * throw null - * GOTO END_OF_METHOD // not checked * } * * 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); diff --git a/jode/jode/flow/CreateClassField.java b/jode/jode/flow/CreateClassField.java index afaa91e..e8aaf39 100644 --- a/jode/jode/flow/CreateClassField.java +++ b/jode/jode/flow/CreateClassField.java @@ -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)) { diff --git a/jode/jode/flow/FlowBlock.java.in b/jode/jode/flow/FlowBlock.java similarity index 80% rename from jode/jode/flow/FlowBlock.java.in rename to jode/jode/flow/FlowBlock.java index b7a2e83..f6892e0 100644 --- a/jode/jode/flow/FlowBlock.java.in +++ b/jode/jode/flow/FlowBlock.java @@ -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.

* 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(); } diff --git a/jode/jode/flow/IfThenElseBlock.java.in b/jode/jode/flow/IfThenElseBlock.java similarity index 98% rename from jode/jode/flow/IfThenElseBlock.java.in rename to jode/jode/flow/IfThenElseBlock.java index 7936a2f..eac9427 100644 --- a/jode/jode/flow/IfThenElseBlock.java.in +++ b/jode/jode/flow/IfThenElseBlock.java @@ -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) diff --git a/jode/jode/flow/InstructionBlock.java.in b/jode/jode/flow/InstructionBlock.java similarity index 97% rename from jode/jode/flow/InstructionBlock.java.in rename to jode/jode/flow/InstructionBlock.java index ad8a4dc..4476a89 100644 --- a/jode/jode/flow/InstructionBlock.java.in +++ b/jode/jode/flow/InstructionBlock.java @@ -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. diff --git a/jode/jode/flow/InstructionContainer.java.in b/jode/jode/flow/InstructionContainer.java similarity index 96% rename from jode/jode/flow/InstructionContainer.java.in rename to jode/jode/flow/InstructionContainer.java index 1925843..9525aef 100644 --- a/jode/jode/flow/InstructionContainer.java.in +++ b/jode/jode/flow/InstructionContainer.java @@ -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 diff --git a/jode/jode/flow/JsrBlock.java b/jode/jode/flow/JsrBlock.java index 6bf2491..d1eb9e8 100644 --- a/jode/jode/flow/JsrBlock.java +++ b/jode/jode/flow/JsrBlock.java @@ -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 */ diff --git a/jode/jode/flow/LoopBlock.java.in b/jode/jode/flow/LoopBlock.java similarity index 99% rename from jode/jode/flow/LoopBlock.java.in rename to jode/jode/flow/LoopBlock.java index 0d050bc..6d52ace 100644 --- a/jode/jode/flow/LoopBlock.java.in +++ b/jode/jode/flow/LoopBlock.java @@ -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; } diff --git a/jode/jode/flow/Makefile.am b/jode/jode/flow/Makefile.am index fe0175d..4cec7ad 100644 --- a/jode/jode/flow/Makefile.am +++ b/jode/jode/flow/Makefile.am @@ -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@ diff --git a/jode/jode/flow/RetBlock.java.in b/jode/jode/flow/RetBlock.java similarity index 95% rename from jode/jode/flow/RetBlock.java.in rename to jode/jode/flow/RetBlock.java index 0823add..f27bb7d 100644 --- a/jode/jode/flow/RetBlock.java.in +++ b/jode/jode/flow/RetBlock.java @@ -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 diff --git a/jode/jode/flow/ReturnBlock.java b/jode/jode/flow/ReturnBlock.java index de0c3de..ba9a0e9 100644 --- a/jode/jode/flow/ReturnBlock.java +++ b/jode/jode/flow/ReturnBlock.java @@ -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; } /** diff --git a/jode/jode/flow/SequentialBlock.java.in b/jode/jode/flow/SequentialBlock.java similarity index 98% rename from jode/jode/flow/SequentialBlock.java.in rename to jode/jode/flow/SequentialBlock.java index 3598fbd..d4caac5 100644 --- a/jode/jode/flow/SequentialBlock.java.in +++ b/jode/jode/flow/SequentialBlock.java @@ -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; diff --git a/jode/jode/flow/SlotSet.java.in b/jode/jode/flow/SlotSet.java similarity index 90% rename from jode/jode/flow/SlotSet.java.in rename to jode/jode/flow/SlotSet.java index 7acf9b6..f5bc852 100644 --- a/jode/jode/flow/SlotSet.java.in +++ b/jode/jode/flow/SlotSet.java @@ -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 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=0; i--) { - /** - * Sort the destinations by finding the greatest destAddr - */ - int index = 0; - for (int j=1; j= 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++) diff --git a/jode/jode/flow/SynchronizedBlock.java.in b/jode/jode/flow/SynchronizedBlock.java similarity index 88% rename from jode/jode/flow/SynchronizedBlock.java.in rename to jode/jode/flow/SynchronizedBlock.java index cdec0f9..f2dc11f 100644 --- a/jode/jode/flow/SynchronizedBlock.java.in +++ b/jode/jode/flow/SynchronizedBlock.java @@ -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; diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java index 1ea93ab..b2b23c2 100644 --- a/jode/jode/flow/TransformConstructors.java +++ b/jode/jode/flow/TransformConstructors.java @@ -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) { diff --git a/jode/jode/flow/TransformExceptionHandlers.java.in b/jode/jode/flow/TransformExceptionHandlers.java similarity index 83% rename from jode/jode/flow/TransformExceptionHandlers.java.in rename to jode/jode/flow/TransformExceptionHandlers.java index 1b20429..e36806e 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java.in +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -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."); } } } diff --git a/jode/jode/flow/TryBlock.java b/jode/jode/flow/TryBlock.java index d8e10f3..1971781 100644 --- a/jode/jode/flow/TryBlock.java +++ b/jode/jode/flow/TryBlock.java @@ -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; diff --git a/jode/jode/flow/VariableSet.java.in b/jode/jode/flow/VariableSet.java similarity index 86% rename from jode/jode/flow/VariableSet.java.in rename to jode/jode/flow/VariableSet.java index 384a3c9..1e3d872 100644 --- a/jode/jode/flow/VariableSet.java.in +++ b/jode/jode/flow/VariableSet.java @@ -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); + } } diff --git a/jode/jode/obfuscator/ClassBundle.java.in b/jode/jode/obfuscator/ClassBundle.java similarity index 93% rename from jode/jode/obfuscator/ClassBundle.java.in rename to jode/jode/obfuscator/ClassBundle.java index c9ba0f6..824ba7b 100644 --- a/jode/jode/obfuscator/ClassBundle.java.in +++ b/jode/jode/obfuscator/ClassBundle.java @@ -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)); diff --git a/jode/jode/obfuscator/ClassIdentifier.java.in b/jode/jode/obfuscator/ClassIdentifier.java similarity index 80% rename from jode/jode/obfuscator/ClassIdentifier.java.in rename to jode/jode/obfuscator/ClassIdentifier.java index 9894f43..f03bcf3 100644 --- a/jode/jode/obfuscator/ClassIdentifier.java.in +++ b/jode/jode/obfuscator/ClassIdentifier.java @@ -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 0 - ? new InnerClassInfo[newExtraCount] : null; - + ClassInfo[] newExtras = new ClassInfo[newExtraCount]; int pos = 0; for (int i=0; i 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)) diff --git a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java similarity index 99% rename from jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in rename to jode/jode/obfuscator/ConstantRuntimeEnvironment.java index fc5346c..59d5430 100644 --- a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in +++ b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java @@ -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 { diff --git a/jode/jode/obfuscator/FieldIdentifier.java.in b/jode/jode/obfuscator/FieldIdentifier.java similarity index 95% rename from jode/jode/obfuscator/FieldIdentifier.java.in rename to jode/jode/obfuscator/FieldIdentifier.java index e552cd4..6cbd144 100644 --- a/jode/jode/obfuscator/FieldIdentifier.java.in +++ b/jode/jode/obfuscator/FieldIdentifier.java @@ -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{ diff --git a/jode/jode/obfuscator/Identifier.java.in b/jode/jode/obfuscator/Identifier.java similarity index 98% rename from jode/jode/obfuscator/Identifier.java.in rename to jode/jode/obfuscator/Identifier.java index 2260f95..a66996d 100644 --- a/jode/jode/obfuscator/Identifier.java.in +++ b/jode/jode/obfuscator/Identifier.java @@ -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 { /** diff --git a/jode/jode/obfuscator/LocalIdentifier.java.in b/jode/jode/obfuscator/LocalIdentifier.java similarity index 93% rename from jode/jode/obfuscator/LocalIdentifier.java.in rename to jode/jode/obfuscator/LocalIdentifier.java index 5f2f860..38356a4 100644 --- a/jode/jode/obfuscator/LocalIdentifier.java.in +++ b/jode/jode/obfuscator/LocalIdentifier.java @@ -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; diff --git a/jode/jode/obfuscator/Main.java.in b/jode/jode/obfuscator/Main.java similarity index 97% rename from jode/jode/obfuscator/Main.java.in rename to jode/jode/obfuscator/Main.java index adcf12e..90f408d 100644 --- a/jode/jode/obfuscator/Main.java.in +++ b/jode/jode/obfuscator/Main.java @@ -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 "+ diff --git a/jode/jode/obfuscator/Makefile.am b/jode/jode/obfuscator/Makefile.am index a839fae..34290fa 100644 --- a/jode/jode/obfuscator/Makefile.am +++ b/jode/jode/obfuscator/Makefile.am @@ -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@ diff --git a/jode/jode/obfuscator/MethodIdentifier.java.in b/jode/jode/obfuscator/MethodIdentifier.java similarity index 98% rename from jode/jode/obfuscator/MethodIdentifier.java.in rename to jode/jode/obfuscator/MethodIdentifier.java index 31146c8..37090d8 100644 --- a/jode/jode/obfuscator/MethodIdentifier.java.in +++ b/jode/jode/obfuscator/MethodIdentifier.java @@ -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; diff --git a/jode/jode/obfuscator/OptionHandler.java.in b/jode/jode/obfuscator/OptionHandler.java similarity index 92% rename from jode/jode/obfuscator/OptionHandler.java.in rename to jode/jode/obfuscator/OptionHandler.java index 9c0723b..1a15a7f 100644 --- a/jode/jode/obfuscator/OptionHandler.java.in +++ b/jode/jode/obfuscator/OptionHandler.java @@ -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) diff --git a/jode/jode/obfuscator/PackageIdentifier.java.in b/jode/jode/obfuscator/PackageIdentifier.java similarity index 93% rename from jode/jode/obfuscator/PackageIdentifier.java.in rename to jode/jode/obfuscator/PackageIdentifier.java index cf239f3..98ad3b1 100644 --- a/jode/jode/obfuscator/PackageIdentifier.java.in +++ b/jode/jode/obfuscator/PackageIdentifier.java @@ -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); diff --git a/jode/jode/obfuscator/Renamer.java.in b/jode/jode/obfuscator/Renamer.java similarity index 94% rename from jode/jode/obfuscator/Renamer.java.in rename to jode/jode/obfuscator/Renamer.java index 6fd588c..407e00a 100644 --- a/jode/jode/obfuscator/Renamer.java.in +++ b/jode/jode/obfuscator/Renamer.java @@ -18,7 +18,9 @@ */ package jode.obfuscator; -import @COLLECTIONS@.Iterator; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef public interface Renamer { diff --git a/jode/jode/obfuscator/ScriptParser.java.in b/jode/jode/obfuscator/ScriptParser.java similarity index 98% rename from jode/jode/obfuscator/ScriptParser.java.in rename to jode/jode/obfuscator/ScriptParser.java index 3204ffb..d60ba45 100644 --- a/jode/jode/obfuscator/ScriptParser.java.in +++ b/jode/jode/obfuscator/ScriptParser.java @@ -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; diff --git a/jode/jode/obfuscator/TranslationTable.java.in b/jode/jode/obfuscator/TranslationTable.java similarity index 78% rename from jode/jode/obfuscator/TranslationTable.java.in rename to jode/jode/obfuscator/TranslationTable.java index 40d8574..660d7e3 100644 --- a/jode/jode/obfuscator/TranslationTable.java.in +++ b/jode/jode/obfuscator/TranslationTable.java @@ -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 { diff --git a/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in b/jode/jode/obfuscator/modules/ConstantAnalyzer.java similarity index 99% rename from jode/jode/obfuscator/modules/ConstantAnalyzer.java.in rename to jode/jode/obfuscator/modules/ConstantAnalyzer.java index 237b87d..85f9cc8 100644 --- a/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in +++ b/jode/jode/obfuscator/modules/ConstantAnalyzer.java @@ -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) diff --git a/jode/jode/obfuscator/modules/KeywordRenamer.java.in b/jode/jode/obfuscator/modules/KeywordRenamer.java similarity index 92% rename from jode/jode/obfuscator/modules/KeywordRenamer.java.in rename to jode/jode/obfuscator/modules/KeywordRenamer.java index 7d1cf0e..4fe00f5 100644 --- a/jode/jode/obfuscator/modules/KeywordRenamer.java.in +++ b/jode/jode/obfuscator/modules/KeywordRenamer.java @@ -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[]; diff --git a/jode/jode/obfuscator/modules/LocalOptimizer.java.in b/jode/jode/obfuscator/modules/LocalOptimizer.java similarity index 99% rename from jode/jode/obfuscator/modules/LocalOptimizer.java.in rename to jode/jode/obfuscator/modules/LocalOptimizer.java index cb776fc..c8928f6 100644 --- a/jode/jode/obfuscator/modules/LocalOptimizer.java.in +++ b/jode/jode/obfuscator/modules/LocalOptimizer.java @@ -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 diff --git a/jode/jode/obfuscator/modules/LocalizeFieldTransformer.java.in b/jode/jode/obfuscator/modules/LocalizeFieldTransformer.java similarity index 100% rename from jode/jode/obfuscator/modules/LocalizeFieldTransformer.java.in rename to jode/jode/obfuscator/modules/LocalizeFieldTransformer.java diff --git a/jode/jode/obfuscator/modules/Makefile.am b/jode/jode/obfuscator/modules/Makefile.am index cf53017..689f276 100644 --- a/jode/jode/obfuscator/modules/Makefile.am +++ b/jode/jode/obfuscator/modules/Makefile.am @@ -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@ diff --git a/jode/jode/obfuscator/modules/ModifierMatcher.java.in b/jode/jode/obfuscator/modules/ModifierMatcher.java similarity index 98% rename from jode/jode/obfuscator/modules/ModifierMatcher.java.in rename to jode/jode/obfuscator/modules/ModifierMatcher.java index 1c8d176..e536d4c 100644 --- a/jode/jode/obfuscator/modules/ModifierMatcher.java.in +++ b/jode/jode/obfuscator/modules/ModifierMatcher.java @@ -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 diff --git a/jode/jode/obfuscator/modules/MultiIdentifierMatcher.java.in b/jode/jode/obfuscator/modules/MultiIdentifierMatcher.java similarity index 97% rename from jode/jode/obfuscator/modules/MultiIdentifierMatcher.java.in rename to jode/jode/obfuscator/modules/MultiIdentifierMatcher.java index 03bb8a4..77b5752 100644 --- a/jode/jode/obfuscator/modules/MultiIdentifierMatcher.java.in +++ b/jode/jode/obfuscator/modules/MultiIdentifierMatcher.java @@ -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 { /** diff --git a/jode/jode/obfuscator/modules/NameSwapper.java.in b/jode/jode/obfuscator/modules/NameSwapper.java similarity index 90% rename from jode/jode/obfuscator/modules/NameSwapper.java.in rename to jode/jode/obfuscator/modules/NameSwapper.java index 5ee0795..172a645 100644 --- a/jode/jode/obfuscator/modules/NameSwapper.java.in +++ b/jode/jode/obfuscator/modules/NameSwapper.java @@ -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 { diff --git a/jode/jode/obfuscator/modules/RemovePopAnalyzer.java.in b/jode/jode/obfuscator/modules/RemovePopAnalyzer.java similarity index 99% rename from jode/jode/obfuscator/modules/RemovePopAnalyzer.java.in rename to jode/jode/obfuscator/modules/RemovePopAnalyzer.java index 2609082..0a0c1cc 100644 --- a/jode/jode/obfuscator/modules/RemovePopAnalyzer.java.in +++ b/jode/jode/obfuscator/modules/RemovePopAnalyzer.java @@ -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() { diff --git a/jode/jode/obfuscator/modules/SerializePreserver.java.in b/jode/jode/obfuscator/modules/SerializePreserver.java similarity index 97% rename from jode/jode/obfuscator/modules/SerializePreserver.java.in rename to jode/jode/obfuscator/modules/SerializePreserver.java index 9703f64..a8f1940 100644 --- a/jode/jode/obfuscator/modules/SerializePreserver.java.in +++ b/jode/jode/obfuscator/modules/SerializePreserver.java @@ -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; diff --git a/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in b/jode/jode/obfuscator/modules/SimpleAnalyzer.java similarity index 96% rename from jode/jode/obfuscator/modules/SimpleAnalyzer.java.in rename to jode/jode/obfuscator/modules/SimpleAnalyzer.java index ec5459c..675f6be 100644 --- a/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in +++ b/jode/jode/obfuscator/modules/SimpleAnalyzer.java @@ -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) { diff --git a/jode/jode/obfuscator/modules/StrongRenamer.java.in b/jode/jode/obfuscator/modules/StrongRenamer.java similarity index 96% rename from jode/jode/obfuscator/modules/StrongRenamer.java.in rename to jode/jode/obfuscator/modules/StrongRenamer.java index 403861b..3c15e60 100644 --- a/jode/jode/obfuscator/modules/StrongRenamer.java.in +++ b/jode/jode/obfuscator/modules/StrongRenamer.java @@ -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 = { diff --git a/jode/jode/obfuscator/modules/UniqueRenamer.java.in b/jode/jode/obfuscator/modules/UniqueRenamer.java similarity index 88% rename from jode/jode/obfuscator/modules/UniqueRenamer.java.in rename to jode/jode/obfuscator/modules/UniqueRenamer.java index c926434..d43effa 100644 --- a/jode/jode/obfuscator/modules/UniqueRenamer.java.in +++ b/jode/jode/obfuscator/modules/UniqueRenamer.java @@ -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; diff --git a/jode/jode/obfuscator/modules/WildCard.java.in b/jode/jode/obfuscator/modules/WildCard.java similarity index 97% rename from jode/jode/obfuscator/modules/WildCard.java.in rename to jode/jode/obfuscator/modules/WildCard.java index d30ce6a..18b3644 100644 --- a/jode/jode/obfuscator/modules/WildCard.java.in +++ b/jode/jode/obfuscator/modules/WildCard.java @@ -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 { diff --git a/jode/jode/swingui/HierarchyTreeModel.java.in b/jode/jode/swingui/HierarchyTreeModel.java similarity index 90% rename from jode/jode/swingui/HierarchyTreeModel.java.in rename to jode/jode/swingui/HierarchyTreeModel.java index 06a019e..2e16cc5 100644 --- a/jode/jode/swingui/HierarchyTreeModel.java.in +++ b/jode/jode/swingui/HierarchyTreeModel.java @@ -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(); diff --git a/jode/jode/swingui/Main.java.in b/jode/jode/swingui/Main.java similarity index 90% rename from jode/jode/swingui/Main.java.in rename to jode/jode/swingui/Main.java index 21bcf36..1cf6ac1 100644 --- a/jode/jode/swingui/Main.java.in +++ b/jode/jode/swingui/Main.java @@ -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 , --classpath "+ + "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 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) diff --git a/jode/jode/type/Makefile.am b/jode/jode/type/Makefile.am index 4ffac52..57761bd 100644 --- a/jode/jode/type/Makefile.am +++ b/jode/jode/type/Makefile.am @@ -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@ diff --git a/jode/jode/type/ReferenceType.java b/jode/jode/type/ReferenceType.java index 74402cb..5ef55a4 100644 --- a/jode/jode/type/ReferenceType.java +++ b/jode/jode/type/ReferenceType.java @@ -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; } diff --git a/jode/jode/type/Type.java.in b/jode/jode/type/Type.java similarity index 99% rename from jode/jode/type/Type.java.in rename to jode/jode/type/Type.java index 6a873e8..811a4e6 100644 --- a/jode/jode/type/Type.java.in +++ b/jode/jode/type/Type.java @@ -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('/','.'))); diff --git a/jode/jode/util/Makefile.am b/jode/jode/util/Makefile.am index da23c66..64fe8d1 100644 --- a/jode/jode/util/Makefile.am +++ b/jode/jode/util/Makefile.am @@ -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) diff --git a/jode/jode/util/SimpleMap.java.in b/jode/jode/util/SimpleMap.java similarity index 94% rename from jode/jode/util/SimpleMap.java.in rename to jode/jode/util/SimpleMap.java index b0ef122..f469095 100644 --- a/jode/jode/util/SimpleMap.java.in +++ b/jode/jode/util/SimpleMap.java @@ -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. diff --git a/jode/jode/util/SimpleSet.java.in b/jode/jode/util/SimpleSet.java similarity index 95% rename from jode/jode/util/SimpleSet.java.in rename to jode/jode/util/SimpleSet.java index f71cbb1..8d5ce57 100644 --- a/jode/jode/util/SimpleSet.java.in +++ b/jode/jode/util/SimpleSet.java @@ -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 { diff --git a/jode/jode/util/StringQuoter.java b/jode/jode/util/StringQuoter.java new file mode 100644 index 0000000..68b7197 --- /dev/null +++ b/jode/jode/util/StringQuoter.java @@ -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+"\'"; + } + } +} diff --git a/jode/jode/util/UnifyHash.java.in b/jode/jode/util/UnifyHash.java similarity index 81% rename from jode/jode/util/UnifyHash.java.in rename to jode/jode/util/UnifyHash.java index 8c5104c..1cadcb9 100644 --- a/jode/jode/util/UnifyHash.java.in +++ b/jode/jode/util/UnifyHash.java @@ -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) {