diff --git a/jode/jode/GlobalOptions.java b/jode/jode/GlobalOptions.java index de63c2f..17dc90d 100644 --- a/jode/jode/GlobalOptions.java +++ b/jode/jode/GlobalOptions.java @@ -80,12 +80,13 @@ public class GlobalOptions { /** * Parse the argument given to the debugging flag. - * @return true, if the argument parsed without problems. + * @exception IllegalArgumentException + * if a problem occured while parsing the argument. */ public static boolean setDebugging(String debuggingString) { if (debuggingString.length() == 0 || debuggingString.equals("help")) { usageDebugging(); - return false; + throw new IllegalArgumentException(); } StringTokenizer st = new StringTokenizer(debuggingString, ","); @@ -98,8 +99,8 @@ public class GlobalOptions { continue next_token; } } - err.println("Illegal debugging flag: "+token); - return false; + throw new IllegalArgumentException("Illegal debugging flag: " + +token); } return true; } diff --git a/jode/jode/decompiler/ClassAnalyzer.java b/jode/jode/decompiler/ClassAnalyzer.java index ce27334..ca8e357 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java +++ b/jode/jode/decompiler/ClassAnalyzer.java @@ -23,6 +23,7 @@ import jode.type.MethodType; import jode.type.Type; import jode.bytecode.ClassFormatException; import jode.bytecode.ClassInfo; +import jode.bytecode.ClassPath; import jode.bytecode.FieldInfo; import jode.bytecode.MethodInfo; import jode.expr.Expression; @@ -72,7 +73,14 @@ public class ClassAnalyzer MethodAnalyzer staticConstructor; MethodAnalyzer[] constructors; + /** + * The outer values for method scoped classes. + */ OuterValues outerValues; + /** + * The outer instance for non-static class scope classes. + */ + Expression outerInstance; public ClassAnalyzer(ClassDeclarer parent, ClassInfo clazz, ImportHandler imports, @@ -97,7 +105,7 @@ public class ClassAnalyzer ? "public" : "all") + " information of " + superClass +" to detect name conflicts."); - ex.printStackTrace(GlobalOptions.err); + GlobalOptions.err.println(ex.toString()); superClass.guess(howMuch); } superClass = superClass.getSuperclass(); @@ -106,26 +114,18 @@ public class ClassAnalyzer 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) { - ClassInfo outerClazz = clazz.getOuterClass(); - if (outerClazz == null) { - if (parent instanceof ClassAnalyzer) - throw new jode.AssertError - ("ClassInfo Attributes are inconsistent: " - + clazz.getName()+" parent: "+parent); - } else { - if (!(parent instanceof ClassAnalyzer) - || ((ClassAnalyzer) parent).clazz != outerClazz) - throw new jode.AssertError - ("ClassInfo Attributes are inconsistent: " - + clazz.getName()+" parent: "+parent); - } - } + /* Check if this is a normal non-static inner class and set + * outerInstance. + */ + if ((Options.options & Options.OPTION_INNER) != 0 + && parent instanceof ClassAnalyzer && !isStatic()) + outerInstance = new ThisOperator(((ClassAnalyzer) parent).clazz); + if (outerValues != null) + this.outerValues = new OuterValues(this, outerValues); } public ClassAnalyzer(ClassDeclarer parent, @@ -141,6 +141,10 @@ public class ClassAnalyzer this(null, clazz, imports); } + public ClassPath getClassPath() { + return clazz.getClassPath(); + } + public final boolean isStatic() { return Modifier.isStatic(modifiers); } @@ -196,6 +200,10 @@ public class ClassAnalyzer return outerValues; } + public Expression getOuterInstance() { + return outerInstance; + } + public void addBlockInitializer(int index, StructuredBlock initializer) { if (blockInitializers[index] == null) blockInitializers[index] = initializer; @@ -218,18 +226,12 @@ public class ClassAnalyzer if ((Options.options & Options.OPTION_INNER) != 0 && innerInfos != null) { /* Create inner classes */ - Expression[] outerThis = new Expression[] { - new ThisOperator(clazz) - }; - int innerCount = innerInfos.length; inners = new ClassAnalyzer[innerCount]; for (int i=0; i < innerCount; i++) { try { inners[i] = new ClassAnalyzer - (this, innerInfos[i], imports, - Modifier.isStatic(innerInfos[i].getModifiers()) - ? null : outerThis); + (this, innerInfos[i], imports, null); } catch (ClassFormatException ex) { GlobalOptions.err.println("Inner class "+innerInfos[i] +" malformed!"); diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java index a59ac7c..f592a78 100644 --- a/jode/jode/decompiler/Decompiler.java +++ b/jode/jode/decompiler/Decompiler.java @@ -42,19 +42,26 @@ public class Decompiler { private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT; + private int tabWidth = 8; + private int indentSize = 4; + private int outputStyle = TabbedPrintWriter.BRACE_AT_EOL; + private int lineWidth = 79; + /** * We need a different pathSeparatorChar, since ':' (used for most * UNIX System) is used a protocol separator in URLs. * * We currently allow both pathSeparatorChar and * altPathSeparatorChar and decide if it is a protocol separator - * by context. + * by context. */ public static final char altPathSeparatorChar = ClassPath.altPathSeparatorChar; /** - * Create a new decompiler. + * Create a new decompiler. Normally you need only one, but you + * can have more around, with different options and different + * class paths. */ public Decompiler() { } @@ -108,14 +115,28 @@ public class Decompiler { */ public void setOption(String option, String value) { if (option.equals("style")) { - if (value.equals("gnu")) - Options.outputStyle = Options.GNU_STYLE; - else if (value.equals("sun")) - Options.outputStyle = Options.SUN_STYLE; - else + if (value.equals("gnu")) { + outputStyle = 0; + indentSize = 2; + } else if (value.equals("sun")) { + outputStyle = TabbedPrintWriter.BRACE_AT_EOL; + indentSize = 4; + } else throw new IllegalArgumentException("Invalid style "+value); return; } + if (option.equals("tabwidth")) { + tabWidth = Integer.parseInt(value); + return; + } + if (option.equals("indent")) { + indentSize = Integer.parseInt(value); + return; + } + if (option.equals("linewidth")) { + lineWidth = Integer.parseInt(value); + return; + } if (option.equals("import")) { int comma = value.indexOf(','); int packLimit = Integer.parseInt(value.substring(0, comma)); @@ -135,6 +156,10 @@ public class Decompiler { GlobalOptions.verboseLevel = Integer.parseInt(value); return; } + if (option.equals("debug")) { + GlobalOptions.setDebugging(value); + return; + } for (int i=0; i < optionStrings.length; i++) { if (option.equals(optionStrings[i])) { if (value.equals("0") @@ -184,8 +209,11 @@ public class Decompiler { ProgressListener progress) throws java.io.IOException { if (classPath == null) { - String cp = System.getProperty("java.class.path") - .replace(File.pathSeparatorChar, altPathSeparatorChar); + String cp = System.getProperty("java.class.path"); + String bootcp = System.getProperty("sun.boot.class.path"); + if (bootcp != null) + cp = bootcp + altPathSeparatorChar + cp; + cp = cp.replace(File.pathSeparatorChar, altPathSeparatorChar); classPath = new ClassPath(cp); } @@ -196,7 +224,9 @@ public class Decompiler { ImportHandler imports = new ImportHandler(importPackageLimit, importClassLimit); TabbedPrintWriter tabbedWriter = - new TabbedPrintWriter(writer, imports, false); + new TabbedPrintWriter(writer, imports, false, + outputStyle, indentSize, + tabWidth, lineWidth); ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports); clazzAna.dumpJavaFile(tabbedWriter, progress); writer.flush(); diff --git a/jode/jode/decompiler/FieldAnalyzer.java b/jode/jode/decompiler/FieldAnalyzer.java index 3db5c48..da30417 100644 --- a/jode/jode/decompiler/FieldAnalyzer.java +++ b/jode/jode/decompiler/FieldAnalyzer.java @@ -51,7 +51,7 @@ public class FieldAnalyzer implements Analyzer { imports = i; modifiers = fd.getModifiers(); - type = Type.tType(fd.getType()); + type = Type.tType(cla.getClassPath(), fd.getType()); fieldName = fd.getName(); constant = null; this.isSynthetic = fd.isSynthetic(); diff --git a/jode/jode/decompiler/ImportHandler.java b/jode/jode/decompiler/ImportHandler.java index 336a7eb..92ddb53 100644 --- a/jode/jode/decompiler/ImportHandler.java +++ b/jode/jode/decompiler/ImportHandler.java @@ -22,7 +22,8 @@ import jode.GlobalOptions; import jode.bytecode.ClassInfo; import jode.type.Type; import jode.type.ArrayType; -import jode.type.ClassInterfacesType; +import jode.type.ClassInfoType; +import jode.type.ClassType; import jode.type.NullType; ///#def COLLECTIONS java.util @@ -274,7 +275,13 @@ public class ImportHandler { clazz = outer; } - String name = clazz.getName(); + useClass(clazz.getName()); + } + + /* Marks the clazz as used, so that it will be imported if used often + * enough. + */ + public void useClass(String name) { Integer i = (Integer) imports.get(name); if (i == null) { @@ -308,8 +315,10 @@ public class ImportHandler { public final void useType(Type type) { if (type instanceof ArrayType) useType(((ArrayType) type).getElementType()); - else if (type instanceof ClassInterfacesType) - useClass(((ClassInterfacesType) type).getClassInfo()); + else if (type instanceof ClassInfoType) + useClass(((ClassInfoType) type).getClassInfo()); + else if (type instanceof ClassType) + useClass(((ClassType) type).getClassName()); } /** @@ -327,7 +336,24 @@ public class ImportHandler { * @return a legal string representation of clazz. */ public String getClassString(ClassInfo clazz) { - String name = clazz.getName(); + return getClassString(clazz.getName()); + } + + /** + * Check if clazz is imported and maybe remove package delimiter from + * full qualified class name. + *
+ * Known Bug 1: If this is called before the imports are cleaned up, + * (that is only for debugging messages), the result is unpredictable. + *
+ * Known Bug 2: It is not checked if the class name conflicts with + * a local variable, field or method name. This is very unlikely + * since the java standard has different naming convention for those + * names. (But maybe an intelligent obfuscator may use this fact.) + * This can only happen with static fields or static methods. + * @return a legal string representation of clazz. + */ + public String getClassString(String name) { if (cachedClassNames == null) /* We are not yet clean, return the full name */ return name; @@ -358,8 +384,10 @@ public class ImportHandler { public String getTypeString(Type type) { if (type instanceof ArrayType) return getTypeString(((ArrayType) type).getElementType()) + "[]"; - else if (type instanceof ClassInterfacesType) - return getClassString(((ClassInterfacesType) type).getClassInfo()); + else if (type instanceof ClassInfoType) + return getClassString(((ClassInfoType) type).getClassInfo()); + else if (type instanceof ClassType) + return getClassString(((ClassType) type).getClassName()); else if (type instanceof NullType) return "Object"; else diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java index 261a548..82121b5 100644 --- a/jode/jode/decompiler/LocalInfo.java +++ b/jode/jode/decompiler/LocalInfo.java @@ -248,7 +248,8 @@ public class LocalInfo implements Declarable { return shadow.getName(); } if (name == null) { - return "local_" + slot + "_" + Integer.toHexString(hashCode()); + return "local_" + (slot >= 0 ? slot + "_" : "") + + Integer.toHexString(hashCode()); } return name; } @@ -312,7 +313,9 @@ public class LocalInfo implements Declarable { && otherType != Type.tError && li.type != Type.tError) { GlobalOptions.err.println("Type error in local " + getName()+": " + li.type + " and " + otherType); - Thread.dumpStack(); + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_TYPES) != 0) + Thread.dumpStack(); } else if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) GlobalOptions.err.println(getName()+" setType, new: "+newType diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java index 9732616..c894518 100644 --- a/jode/jode/decompiler/Main.java +++ b/jode/jode/decompiler/Main.java @@ -25,6 +25,7 @@ import jode.GlobalOptions; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.zip.ZipOutputStream; @@ -47,7 +48,6 @@ public class Main extends Options { new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'), - new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'), new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null, OPTION_START+0), new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null, @@ -86,40 +86,6 @@ public class Main extends Options { "The directories should be separated by ','."); err.println(" -d, --dest
A list of local variables that a method scoped class inherits + * from its declaring method.
* - * A method scoped class is a class that is declared in a method and - * it can access other (final) local variables declared earlier. To - * realize this the java compiler adds hidden parameters to the + *A method scoped class is a class that is declared in a method + * and it can access other (final) local variables declared earlier. + * To realize this the java compiler adds hidden parameters to the * constructor of the method scoped class, where it passes the values * of the local varaiables. If a method scoped class has more than * one constructor, each gets this hidden parameters. These hidden * parameters are the outerValues, because they are used to transport - * a value of a local variable from an outer method. + * a value of a local variable from an outer method.
* - * Unfortunately there is no definite way to distinguish this outer + *Unfortunately there is no definite way to distinguish this outer * value parameters from the real parameters, so jode has to do a * guess: It first assumes that everything is an outer value parameter * added by the compiler and if this leads to contradiction shrinks * the count of these parameters. A contradiction can occur, because - * the constructor is called two times with different values. + * the constructor is called two times with different values.
* - * On the other hand the TransformConstructor class assumes at some + *On the other hand the TransformConstructor class assumes at some * point that some parameters are outer values. If later a - * contradiction occurs, jode has to give up and complain loudly. + * contradiction occurs, jode has to give up and complain loudly.
* - * Every class interested in outer values, may register itself as + *Every class interested in outer values, may register itself as * OuterValueListener. It will then be notified every time the outer * values shrink. Sometimes there are real listener queues: if * another method scoped class creates instances of the first in its @@ -63,28 +63,26 @@ import java.util.Enumeration; * the second class's constructor is really an outer value, we have to * add a listener. If later a constructor invokation for the second * class is found, where a parameter does not have the right outer - * value, the listener will also shrink the outer values list of the - * first class. + * value, the listener will also shrink the outer values list of the + * first class.
* - * A non static _class_ scoped class (i.e. a normal inner class) also + *A non static _class_ scoped class (i.e. a normal inner class) also
* has a hidden parameter, namely the instance of its outer class.
* This hidden parameter is not considered as outer value though.
* Note that you can even explicitly invoke the constructor with a
* different outer class instance, by using the
* outerInstance.new InnerClass()
construct. This
- * exception doesn't apply to method scoped classes, though.
+ * exception doesn't apply to method scoped classes, though.
Anonymous classes can of course also extend class or method scoped * classes. If they are compiled by jikes the constructor takes as * last parameter the outer instance of its super class. This should * really be the first parameter just after the outerValues, as it * is under javac. We mark such classes as jikesAnonymousInner. This - * is done in the initialize() pass. + * is done in the initialize() pass.
* - * @see #shrinkOuterValues * @see #addOuterValueListener - * @since 1.0.93 - */ + * @since 1.0.93 */ public class OuterValues { private ClassAnalyzer clazzAnalyzer; @@ -331,7 +329,6 @@ public class OuterValues public void setCount(int newHeadCount) { if (newHeadCount >= headCount) return; - headCount = newHeadCount; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) { @@ -339,6 +336,7 @@ public class OuterValues new Throwable().printStackTrace(GlobalOptions.err); } + headCount = newHeadCount; if (newHeadCount < headMinCount) { GlobalOptions.err.println ("WARNING: something got wrong with scoped class " @@ -374,5 +372,3 @@ public class OuterValues return sb.append("]").toString(); } } - - diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java index f9f632d..8e342b7 100644 --- a/jode/jode/decompiler/TabbedPrintWriter.java +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -30,8 +30,9 @@ import jode.type.*; public class TabbedPrintWriter { /* The indentation size. */ private int indentsize; - /* The size of a tab, MAXINT if we shouldn't use tabs at all. */ + /* The size of a tab, 0 if we shouldn't use tabs at all. */ private int tabWidth; + private int style; private int lineWidth; private int currentIndent = 0; private String indentStr = ""; @@ -39,40 +40,48 @@ public class TabbedPrintWriter { private ImportHandler imports; private Stack scopes = new Stack(); + public static final int BRACE_AT_EOL = 0x10; + + /** + * This string contains a few tab characters followed by tabWidth - 1 + * spaces. It is used to quickly calculate the indentation string. + */ + private String tabSpaceString; private StringBuffer currentLine; private BreakPoint currentBP; + public final static int EXPL_PAREN = 0; public final static int NO_PAREN = 1; public final static int IMPL_PAREN = 2; public final static int DONT_BREAK = 3; + /** + * The amount of tabs for which we can use the tabSpaceString. + */ + private final static int FASTINDENT = 20; + /** * Convert the numeric indentation to a string. */ protected String makeIndentStr(int indent) { - String tabSpaceString = /* (tab x 20) . (space x 20) */ - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t "; if (indent < 0) return "NEGATIVEINDENT"+indent; int tabs = indent / tabWidth; indent -= tabs * tabWidth; - if (tabs <= 20 && indent <= 20) { + if (tabs <= FASTINDENT) { /* The fast way. */ - return tabSpaceString.substring(20 - tabs, 20 + indent); + return tabSpaceString.substring(FASTINDENT - tabs, + FASTINDENT + indent); } else { /* the not so fast way */ StringBuffer sb = new StringBuffer(tabs + indent); - while (tabs > 20) { - sb.append(tabSpaceString.substring(0,20)); + while (tabs > FASTINDENT) { + sb.append(tabSpaceString.substring(0, FASTINDENT)); tabs -= 20; } - sb.append(tabSpaceString.substring(0, tabs)); - while (indent > 20) { - sb.append(tabSpaceString.substring(20)); - indent -= 20; - } - sb.append(tabSpaceString.substring(40 - indent)); + sb.append(tabSpaceString.substring(FASTINDENT - tabs, + FASTINDENT + indent)); return sb.toString(); } } @@ -101,7 +110,7 @@ public class TabbedPrintWriter { public void startOp(int opts, int penalty, int pos) { if (startPos != -1) { - System.err.println("WARNING: missing breakOp"); + GlobalOptions.err.println("WARNING: missing breakOp"); Thread.dumpStack(); return; } @@ -119,17 +128,14 @@ public class TabbedPrintWriter { public void endOp(int pos) { endPos = pos; if (childBPs.size() == 1) { - BreakPoint child = - (BreakPoint) currentBP.childBPs.elementAt(0); - if (child.startPos == -1) { - startPos = endPos = -1; - childBPs = null; - } else if (child.startPos == currentBP.startPos - && child.endPos == currentBP.endPos) { - if (options == DONT_BREAK) - options = child.options; - childBPs = child.childBPs; - } + /* There is no breakpoint in this op, replace this with + * our child, if possible. + */ + BreakPoint child = (BreakPoint) childBPs.elementAt(0); + startPos = child.startPos; + endPos = child.endPos; + breakPenalty = child.breakPenalty; + childBPs = child.childBPs; } } @@ -217,7 +223,7 @@ public class TabbedPrintWriter { } public BreakPoint commitMinPenalty(int space, int lastSpace, - int minPenalty) { + int minPenalty) { if (startPos == -1 || lastSpace > endPos - startPos || minPenalty == 10 * (endPos - startPos - lastSpace)) { /* We don't have to break anything */ @@ -231,8 +237,6 @@ public class TabbedPrintWriter { /* penalty if we are breaking the line here. */ int breakPen = getBreakPenalty(space, lastSpace, minPenalty + 1); -// pw.print("commit[bp="+breakPen+";"+minPenalty+";" -// +space+","+lastSpace+"]"); if (minPenalty == breakPen) { commitBreakPenalty(space, lastSpace, breakPen); return this; @@ -256,16 +260,11 @@ public class TabbedPrintWriter { return child; } } - pw.println("XXXXXXXXXXX CAN'T COMMIT"); - startPos = -1; - childBPs = null; - return this; + throw new IllegalStateException("Can't commit line break!"); } public int getMinPenalty(int space, int lastSpace, int minPenalty) { -// pw.print("getMinPenalty["+startPos+","+endPos+"]("+space+","+lastSpace+","+minPenalty+") "); if (10 * -lastSpace >= minPenalty) { -// pw.println("= minPenalty"); return minPenalty; } @@ -273,20 +272,16 @@ public class TabbedPrintWriter { return 10 * -lastSpace; if (lastSpace > endPos - startPos) { -// pw.println("= NULL"); return 0; } if (minPenalty <= 1) { -// pw.println("= ONE"); return minPenalty; } if (minPenalty > 10 * (endPos - startPos - lastSpace)) minPenalty = 10 * (endPos - startPos - lastSpace); -// pw.print("[mp="+minPenalty+"]"); - int size = childBPs.size(); if (size == 0) return minPenalty; @@ -294,7 +289,6 @@ public class TabbedPrintWriter { if (size > 1 && options != DONT_BREAK) { /* penalty if we are breaking at this level. */ minPenalty = getBreakPenalty(space, lastSpace, minPenalty); -// pw.print("[bp="+minPenalty+"]"); } /* penalty if we are breaking only one child */ @@ -308,15 +302,11 @@ public class TabbedPrintWriter { lastSpace - front - tail, minPenalty - penalty); } -// pw.println("= "+minPenalty); return minPenalty; } public void commitBreakPenalty(int space, int lastSpace, int minPenalty) { -// pw.println("commitBreakPenalty: "+startPos+","+endPos+";" -// +space+","+lastSpace+";"+minPenalty); - if (options == IMPL_PAREN) { space--; lastSpace -= 2; @@ -469,42 +459,75 @@ public class TabbedPrintWriter { } public TabbedPrintWriter (OutputStream os, ImportHandler imports, - boolean autoFlush) { + boolean autoFlush, int style, + int indentSize, int tabWidth, int lineWidth) { pw = new PrintWriter(os, autoFlush); this.imports = imports; + this.style = style; + this.indentsize = indentSize; + this.tabWidth = tabWidth; + this.lineWidth = lineWidth; init(); } public TabbedPrintWriter (Writer os, ImportHandler imports, - boolean autoFlush) { + boolean autoFlush, int style, + int indentSize, int tabWidth, int lineWidth) { pw = new PrintWriter(os, autoFlush); this.imports = imports; + this.style = style; + this.indentsize = indentSize; + this.tabWidth = tabWidth; + this.lineWidth = lineWidth; init(); } + public TabbedPrintWriter (OutputStream os, ImportHandler imports, + boolean autoFlush) { + this(os, imports, autoFlush, BRACE_AT_EOL, 4, 8, 79); + } + + public TabbedPrintWriter (Writer os, ImportHandler imports, + boolean autoFlush) { + this(os, imports, autoFlush, BRACE_AT_EOL, 4, 8, 79); + } + public TabbedPrintWriter (OutputStream os, ImportHandler imports) { - this(os, imports, true); + this(os, imports, true, BRACE_AT_EOL, 4, 8, 79); } public TabbedPrintWriter (Writer os, ImportHandler imports) { - this(os, imports, true); + this(os, imports, true, BRACE_AT_EOL, 4, 8, 79); } public TabbedPrintWriter (OutputStream os) { - this(os, null); + this(os, null, true, BRACE_AT_EOL, 4, 8, 79); } public TabbedPrintWriter (Writer os) { - this(os, null); + this(os, null, true, BRACE_AT_EOL, 4, 8, 79); } - public void init() { - this.indentsize = (Options.outputStyle & Options.TAB_SIZE_MASK); - this.tabWidth = 8; - this.lineWidth = 79; + private void init() { currentLine = new StringBuffer(); currentBP = new BreakPoint(null, 0); currentBP.startOp(DONT_BREAK, 1, 0); + initTabString(); + } + + private void initTabString() { + char tabChar = '\t'; + if (tabWidth == 0) { + /* If tabWidth is 0 use spaces instead of tabs. */ + tabWidth = 1; + tabChar = ' '; + } + StringBuffer sb = new StringBuffer(FASTINDENT + tabWidth - 1); + for (int i = 0; i < FASTINDENT; i++) + sb.append(tabChar); + for (int i = 0; i < tabWidth - 1; i++) + sb.append(' '); + tabSpaceString = sb.toString(); } public void tab() { @@ -680,9 +703,19 @@ public class TabbedPrintWriter { public String getTypeString(Type type) { if (type instanceof ArrayType) return getTypeString(((ArrayType) type).getElementType()) + "[]"; - else if (type instanceof ClassInterfacesType) { - ClassInfo clazz = ((ClassInterfacesType) type).getClassInfo(); + else if (type instanceof ClassInfoType) { + ClassInfo clazz = ((ClassInfoType) type).getClassInfo(); return getClassString(clazz, Scope.CLASSNAME); + } else if (type instanceof ClassType) { + String name = ((ClassType) type).getClassName(); + if (imports != null) { + String importedName = imports.getClassString(name); + if (!conflicts(importedName, null, Scope.CLASSNAME)) + return importedName; + } + if (conflicts(name, null, Scope.AMBIGUOUSNAME)) + return "PKGNAMECONFLICT "+ name; + return name; } else if (type instanceof NullType) return "Object"; else @@ -695,7 +728,7 @@ public class TabbedPrintWriter { * brace. It doesn't do a tab stop after opening the brace. */ public void openBrace() { - if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) { + if ((style & BRACE_AT_EOL) != 0) { print(currentLine.length() > 0 ? " {" : "{"); println(); } else { @@ -713,7 +746,7 @@ public class TabbedPrintWriter { * brace. It doesn't do a tab stop after opening the brace. */ public void openBraceNoSpace() { - if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) + if ((style & BRACE_AT_EOL) != 0) println("{"); else { if (currentLine.length() > 0) @@ -725,7 +758,7 @@ public class TabbedPrintWriter { } public void closeBraceContinue() { - if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) + if ((style & BRACE_AT_EOL) != 0) print("} "); else { println("}"); @@ -735,7 +768,7 @@ public class TabbedPrintWriter { } public void closeBraceNoSpace() { - if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) + if ((style & BRACE_AT_EOL) != 0) print("}"); else { println("}"); @@ -745,7 +778,7 @@ public class TabbedPrintWriter { } public void closeBrace() { - if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) + if ((style & BRACE_AT_EOL) != 0) println("}"); else { println("}"); diff --git a/jode/jode/expr/Expression.java b/jode/jode/expr/Expression.java index 7410dcb..7dd15a7 100644 --- a/jode/jode/expr/Expression.java +++ b/jode/jode/expr/Expression.java @@ -301,6 +301,7 @@ public abstract class Expression { dumpExpression(writer); } catch (RuntimeException ex) { writer.print("(RUNTIME ERROR IN EXPRESSION)"); + ex.printStackTrace(); } if (needEndOp2) { diff --git a/jode/jode/expr/FieldOperator.java b/jode/jode/expr/FieldOperator.java index 0f0de28..0e0485e 100644 --- a/jode/jode/expr/FieldOperator.java +++ b/jode/jode/expr/FieldOperator.java @@ -21,9 +21,10 @@ package jode.expr; import jode.GlobalOptions; import jode.type.Type; import jode.type.NullType; -import jode.type.ClassInterfacesType; +import jode.type.ClassInfoType; import jode.bytecode.FieldInfo; import jode.bytecode.ClassInfo; +import jode.bytecode.ClassPath; import jode.bytecode.Reference; import jode.decompiler.MethodAnalyzer; import jode.decompiler.ClassAnalyzer; @@ -48,14 +49,19 @@ public abstract class FieldOperator extends Operator { Reference ref; Type classType; ClassInfo classInfo; + ClassPath classPath; String callerPackage; public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag, Reference ref) { - super(Type.tType(ref.getType())); + super(Type.tUnknown); + + this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath(); + this.methodAnalyzer = methodAnalyzer; this.staticFlag = staticFlag; - this.classType = Type.tType(ref.getClazz()); + this.type = Type.tType(classPath, ref.getType()); + this.classType = Type.tType(classPath, ref.getClazz()); this.ref = ref; if (staticFlag) methodAnalyzer.useType(classType); @@ -64,8 +70,8 @@ public abstract class FieldOperator extends Operator { callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName(); int dot = callerPackage.lastIndexOf('.'); callerPackage = callerPackage.substring(0, dot); - if (classType instanceof ClassInterfacesType) { - classInfo = ((ClassInterfacesType) classType).getClassInfo(); + if (classType instanceof ClassInfoType) { + classInfo = ((ClassInfoType) classType).getClassInfo(); if ((Options.options & Options.OPTION_ANON) != 0 || (Options.options & Options.OPTION_INNER) != 0) { try { @@ -111,7 +117,7 @@ public abstract class FieldOperator extends Operator { while (true) { if (clazz == ana.getClazz()) { int field = ana.getFieldIndex - (ref.getName(), Type.tType(ref.getType())); + (ref.getName(), Type.tType(classPath, ref.getType())); if (field >= 0) return ana.getField(field); return null; @@ -135,7 +141,7 @@ public abstract class FieldOperator extends Operator { } public Type getFieldType() { - return Type.tType(ref.getType()); + return Type.tType(classPath, ref.getType()); } private FieldInfo[] loadFields(ClassInfo clazz) { @@ -156,12 +162,12 @@ public abstract class FieldOperator extends Operator { public boolean needsCast(Type type) { if (type instanceof NullType) return true; - if (!(type instanceof ClassInterfacesType - && classType instanceof ClassInterfacesType)) + if (!(type instanceof ClassInfoType + && classType instanceof ClassInfoType)) return false; - ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo(); - ClassInfo parClazz = ((ClassInterfacesType) type).getClassInfo(); + ClassInfo clazz = ((ClassInfoType) classType).getClassInfo(); + ClassInfo parClazz = ((ClassInfoType) type).getClassInfo(); while (clazz != parClazz && clazz != null) { FieldInfo[] fields = parClazz.getFields(); for (int i = 0; i < fields.length; i++) { diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java index 586460e..bfb1cd2 100644 --- a/jode/jode/expr/IfThenElseOperator.java +++ b/jode/jode/expr/IfThenElseOperator.java @@ -19,6 +19,7 @@ package jode.expr; import jode.type.Type; +import jode.bytecode.ClassPath; import jode.decompiler.FieldAnalyzer; import jode.decompiler.TabbedPrintWriter; @@ -86,10 +87,11 @@ public class IfThenElseOperator extends Operator { .equals(Type.tString))) { String clazz = (String) ((ConstOperator)invoke.subExpressions[0]).getValue(); + ClassPath cp = field.getClassAnalyzer().getClassPath(); if (field.setClassConstant(clazz)) - return new ClassFieldOperator(clazz.charAt(0) == '[' - ? Type.tType(clazz) - : Type.tClass(clazz)); + return new ClassFieldOperator + (clazz.charAt(0) == '[' + ? Type.tType(cp, clazz) : Type.tClass(cp, clazz)); } } } diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java index 2c8b1d4..fe52e3f 100644 --- a/jode/jode/expr/InvokeOperator.java +++ b/jode/jode/expr/InvokeOperator.java @@ -63,9 +63,10 @@ public final class InvokeOperator extends Operator MethodType methodType; String methodName; int skippedArgs; - Type classType; + ClassType classType; Type[] hints; ClassInfo classInfo; + ClassPath classPath; String callerPackage; /** @@ -105,6 +106,20 @@ public final class InvokeOperator extends Operator Type[] hint0C = new Type[] { null, tCharHint }; Type[] hint0C0 = new Type[] { null, tCharHint, null }; + ClassType tWriter = + Type.tSystemClass("java.io.Writer", + Type.tObject, Type.EMPTY_IFACES, false, false); + ClassType tReader = + Type.tSystemClass("java.io.Reader", + Type.tObject, Type.EMPTY_IFACES, false, false); + ClassType tFilterReader = + Type.tSystemClass("java.io.FilterReader", + tReader, Type.EMPTY_IFACES, false, false); + ClassType tPBReader = + Type.tSystemClass("java.io.PushBackReader", + tFilterReader, Type.EMPTY_IFACES, + false, false); + Map hintString0CMap = new SimpleMap (Collections.singleton (new SimpleMap.SimpleEntry(Type.tString, hint0C))); @@ -118,24 +133,26 @@ public final class InvokeOperator extends Operator hintTypes.put("write.(I)V", new SimpleMap (Collections.singleton (new SimpleMap.SimpleEntry - (Type.tClass("java.io.Writer"), hint0C)))); + (tWriter, hint0C)))); hintTypes.put("read.()I", new SimpleMap (Collections.singleton (new SimpleMap.SimpleEntry - (Type.tClass("java.io.Reader"), hintC)))); + (tReader, hintC)))); hintTypes.put("unread.(I)V", new SimpleMap (Collections.singleton (new SimpleMap.SimpleEntry - (Type.tClass("java.io.PushbackReader"), hint0C)))); + (tPBReader, hint0C)))); } public InvokeOperator(MethodAnalyzer methodAnalyzer, int methodFlag, Reference reference) { super(Type.tUnknown, 0); - this.methodType = Type.tMethod(reference.getType()); + this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath(); + this.methodType = Type.tMethod(classPath, reference.getType()); this.methodName = reference.getName(); - this.classType = Type.tType(reference.getClazz()); + this.classType = (ClassType) + Type.tType(classPath, reference.getClazz()); this.hints = null; Map allHints = (Map) hintTypes.get(methodName+"."+methodType); if (allHints != null) { @@ -161,8 +178,8 @@ public final class InvokeOperator extends Operator callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName(); int dot = callerPackage.lastIndexOf('.'); callerPackage = callerPackage.substring(0, dot); - if (classType instanceof ClassInterfacesType) { - classInfo = ((ClassInterfacesType) classType).getClassInfo(); + if (classType instanceof ClassInfoType) { + classInfo = ((ClassInfoType) classType).getClassInfo(); if ((Options.options & Options.OPTION_ANON) != 0 || (Options.options & Options.OPTION_INNER) != 0) { try { @@ -175,6 +192,10 @@ public final class InvokeOperator extends Operator } } + public final ClassPath getClassPath() { + return classPath; + } + public final boolean isStatic() { return methodFlag == STATIC; } @@ -207,13 +228,13 @@ public final class InvokeOperator extends Operator public void updateSubTypes() { int offset = 0; if (!isStatic()) { - subExpressions[offset++].setType(Type.tSubType(getClassType())); + subExpressions[offset++].setType(getClassType().getSubType()); } Type[] paramTypes = methodType.getParameterTypes(); for (int i=0; i < paramTypes.length; i++) { Type pType = (hints != null && hints[i+1] != null) ? hints[i+1] : paramTypes[i]; - subExpressions[offset++].setType(Type.tSubType(pType)); + subExpressions[offset++].setType(pType.getSubType()); } } @@ -320,11 +341,10 @@ public final class InvokeOperator extends Operator * outer instance. */ public boolean isOuter() { - if (classType instanceof ClassInterfacesType) { - ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo(); + if (classInfo != null) { ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); while (true) { - if (clazz == ana.getClazz()) + if (classInfo == ana.getClazz()) return true; if (ana.getParent() == null) break; @@ -362,17 +382,8 @@ public final class InvokeOperator extends Operator * Checks, whether this is a call of a method from the super class. */ public boolean isSuperOrThis() { - ClassInfo clazz = getClassInfo(); - if (clazz != null) { - try { - return clazz.superClassOf(methodAnalyzer.getClazz()); - } catch (IOException ex) { - /* Assume it is not a super class. This will print - * a warning. - */ - } - } - return false; + return classType.maybeSubTypeOf + (Type.tClass(methodAnalyzer.getClazz())); } public boolean isConstant() { @@ -664,11 +675,11 @@ public final class InvokeOperator extends Operator realClassType = paramTypes[0]; } - if (!(realClassType instanceof ClassInterfacesType)) { + if (!(realClassType instanceof ClassInfoType)) { /* Arrays don't have overloaded methods, all okay */ return false; } - ClassInfo clazz = ((ClassInterfacesType) realClassType).getClassInfo(); + ClassInfo clazz = ((ClassInfoType) realClassType).getClassInfo(); int offset = skippedArgs; Type[] myParamTypes = methodType.getParameterTypes(); @@ -687,7 +698,8 @@ public final class InvokeOperator extends Operator continue next_method; Type[] otherParamTypes - = Type.tMethod(methods[i].getType()).getParameterTypes(); + = Type.tMethod(classPath, methods[i].getType()) + .getParameterTypes(); if (otherParamTypes.length != myParamTypes.length) { /* parameter count doesn't match*/ continue next_method; @@ -783,7 +795,8 @@ public final class InvokeOperator extends Operator } } - if ((Options.options & Options.OPTION_INNER) != 0 + if ((~Options.options & (Options.OPTION_INNER + | Options.OPTION_CONTRAFO)) == 0 && clazz.getOuterClass() != null && !Modifier.isStatic(clazz.getModifiers())) { @@ -873,8 +886,7 @@ public final class InvokeOperator extends Operator /* clazz != null, since an array doesn't have a constructor */ clazzAna = methodAnalyzer.getClassAnalyzer(clazz); - if ((~Options.options & - (Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0 + if ((Options.options & Options.OPTION_ANON) != 0 && clazzAna != null && clazz.isMethodScoped()) { /* This is a known method scoped class, skip the outerValues */ diff --git a/jode/jode/expr/NewArrayOperator.java b/jode/jode/expr/NewArrayOperator.java index 4fa7dd5..2ed5895 100644 --- a/jode/jode/expr/NewArrayOperator.java +++ b/jode/jode/expr/NewArrayOperator.java @@ -57,6 +57,7 @@ public class NewArrayOperator extends Operator { writer.print("new "); writer.printType(flat.getHint()); for (int i=0; i< depth; i++) { + writer.breakOp(); writer.print("["); if (i < subExpressions.length) subExpressions[i].dumpExpression(writer, 0); diff --git a/jode/jode/flow/CreateClassField.java b/jode/jode/flow/CreateClassField.java index e8aaf39..e707e12 100644 --- a/jode/jode/flow/CreateClassField.java +++ b/jode/jode/flow/CreateClassField.java @@ -19,6 +19,7 @@ package jode.flow; import jode.expr.*; +import jode.bytecode.ClassPath; import jode.type.Type; import jode.decompiler.LocalInfo; @@ -71,10 +72,11 @@ public class CreateClassField { && ((ConstOperator)param).getValue() instanceof String) { String clazz = (String) ((ConstOperator)param).getValue(); if (put.getField().setClassConstant(clazz)) { + ClassPath cp = invoke.getClassPath(); cmp.setSubExpressions (0, new ClassFieldOperator(clazz.charAt(0) == '[' - ? Type.tType(clazz) - : Type.tClass(clazz))); + ? Type.tType(cp, clazz) + : Type.tClass(cp, clazz))); EmptyBlock empty = new EmptyBlock(); empty.moveJump(ifBlock.thenBlock.jump); ifBlock.setThenBlock(empty); diff --git a/jode/jode/flow/FlowBlock.java b/jode/jode/flow/FlowBlock.java index f6892e0..083ab05 100644 --- a/jode/jode/flow/FlowBlock.java +++ b/jode/jode/flow/FlowBlock.java @@ -806,7 +806,7 @@ public class FlowBlock { try { if (block.outer != null || block.flowBlock != this) { - throw new AssertError("Inconsistency"); + throw new AssertError("Inconsistency: outer:"+block.outer+" block.flow"+block.flowBlock +" this: "+this); } block.checkConsistent(); @@ -869,7 +869,7 @@ public class FlowBlock { } public void prependBlock(StructuredBlock insertBlock) { - lastModified = insertBlock.appendBlock(block); + lastModified = block = insertBlock.appendBlock(block); SlotSet blockIn = new SlotSet(); SlotSet blockKill = new SlotSet(); VariableSet blockGen = new VariableSet(); @@ -1405,8 +1405,9 @@ public class FlowBlock { boolean predOutOfRange = false; for (Iterator i = succ.predecessors.iterator(); i.hasNext(); ) { - int predBlockNr = ((FlowBlock)i.next()).blockNr; - if (predBlockNr < start || predBlockNr >= end) { + FlowBlock pred = (FlowBlock)i.next(); + if (pred == null /* the start marker */ + || pred.blockNr < start || pred.blockNr >= end) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) @@ -1552,10 +1553,14 @@ public class FlowBlock { /** * Mark the flow block as first flow block in a method. */ - public void makeStartBlock() { + public void addStartPred() { predecessors.add(null); } + public void removeStartPred() { + predecessors.remove(null); + } + public void removeSuccessor(Jump jump) { SuccessorInfo destInfo = (SuccessorInfo) successors.get(jump.destination); @@ -1675,6 +1680,9 @@ public class FlowBlock { private void promoteInSets() { for (Iterator i = predecessors.iterator(); i.hasNext(); ) { FlowBlock pred = (FlowBlock) i.next(); + /* Skip the start marker */ + if (pred == null) + continue; SuccessorInfo succInfo = (SuccessorInfo) pred.successors.get(this); /* First get the gen/kill sets of all jumps of predecessor @@ -1743,7 +1751,7 @@ public class FlowBlock { public void dumpSource(TabbedPrintWriter writer) throws java.io.IOException { - if (predecessors.size() != 0) { + if (predecessors.size() != 0) { writer.untab(); writer.println(getLabel()+":"); writer.tab(); diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java index b2b23c2..1466617 100644 --- a/jode/jode/flow/TransformConstructors.java +++ b/jode/jode/flow/TransformConstructors.java @@ -99,8 +99,6 @@ public class TransformConstructors { OuterValues outerValues; - boolean jikesAnonInner = false; - public TransformConstructors(ClassAnalyzer clazzAnalyzer, boolean isStatic, MethodAnalyzer[] cons) { this.clazzAnalyzer = clazzAnalyzer; @@ -158,10 +156,16 @@ public class TransformConstructors { return 1; } - public void lookForConstructorCall() { + private void lookForConstructorCall() { type01Count = cons.length; + for (int i=0; i< type01Count; ) { MethodAnalyzer current = cons[i]; + if ((Options.options & Options.OPTION_CONTRAFO) != 0 + && clazzAnalyzer.getOuterInstance() != null) + current.getParamInfo(1).setExpression + (clazzAnalyzer.getOuterInstance()); + FlowBlock header = cons[i].getMethodHeader(); /* Check that code block is fully analyzed */ if (header == null || !header.hasNoJumps()) @@ -261,13 +265,14 @@ public class TransformConstructors { /* slot counts from last slot down. */ int start = 1; - if (subExpr.length > 2) { + if (subExpr.length >= 2) { LocalLoadOperator llop = (LocalLoadOperator) subExpr[1]; if (llop.getLocalInfo().getSlot() == slot) { jikesAnon = true; start++; // This is not an outer value. + outerValues.setCount(params.length - 1); minOuter--; slot -= params[minOuter - 1].stackSize(); } @@ -370,6 +375,7 @@ public class TransformConstructors { private Expression renameJikesSuper(Expression expr, MethodAnalyzer methodAna, + Expression outer0, int firstOuterSlot, int firstParamSlot) { if (expr instanceof LocalLoadOperator) { @@ -377,7 +383,9 @@ public class TransformConstructors { int slot = llop.getLocalInfo().getSlot(); if (slot >= firstOuterSlot && slot < firstParamSlot) return outerValues.getValueBySlot(slot); - else { + else if (slot == 1) { + return outer0; + } else { Type[] paramTypes = methodAna.getType().getParameterTypes(); int param; /* Adjust the slot */ @@ -394,7 +402,7 @@ public class TransformConstructors { Expression subExpr[] = ((Operator)expr).getSubExpressions(); for (int i=0; i< subExpr.length; i++) { Expression newSubExpr = - renameJikesSuper(subExpr[i], methodAna, + renameJikesSuper(subExpr[i], methodAna, outer0, firstOuterSlot, firstParamSlot); if (newSubExpr != subExpr[i]) ((Operator)expr).setSubExpressions(i, newSubExpr); @@ -403,7 +411,7 @@ public class TransformConstructors { return expr; } - public void checkJikesContinuation() { + private void checkJikesContinuation() { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) System.err.println("checkJikesContinuation: "+outerValues); @@ -441,6 +449,7 @@ public class TransformConstructors { Vector localLoads = null; InstructionBlock superBlock = null; + InvokeOperator superInvoke = null; if (i >= type0Count) { /* Extract the super() or this() call at the beginning * of the constructor @@ -452,15 +461,10 @@ public class TransformConstructors { superBlock = (InstructionBlock) sb.getSubBlocks()[0]; sb = sb.getSubBlocks()[1]; - Expression superExpr = superBlock.getInstruction().simplify(); - InvokeOperator superInvoke = (InvokeOperator) superExpr; - superBlock.setInstruction(superInvoke); - - Expression[] subExpr = superInvoke.getSubExpressions(); - for (int j=1; j< subExpr.length; j++) { - if (!checkJikesSuper(subExpr[j])) - continue constr_loop; - } + superInvoke = (InvokeOperator) + superBlock.getInstruction().simplify(); + if (!checkJikesSuper(superInvoke)) + continue constr_loop; } if (!(sb instanceof InstructionBlock)) @@ -544,11 +548,19 @@ public class TransformConstructors { /* Now move the constructor call. */ if (superBlock != null) { - Expression newExpr = - renameJikesSuper(superBlock.getInstruction(), methodAna, + InvokeOperator newSuper = (InvokeOperator) + renameJikesSuper(superInvoke, methodAna, outer0, firstOuterSlot, firstParamSlot); superBlock.removeBlock(); - methodAna.insertStructuredBlock(superBlock); + if (i > type0Count) { + cons[i] = cons[type0Count]; + cons[type0Count] = constr; + } + type0Count++; + if (!isDefaultSuper(newSuper)) { + superBlock.setInstruction(newSuper); + methodAna.insertStructuredBlock(superBlock); + } } if (outer0 != null) { methodAna.getParamInfo(1).setExpression(outer0); @@ -573,7 +585,7 @@ public class TransformConstructors { * @param expr the initializer to check * @return the transformed initializer or null if expr is not valid. */ - public Expression transformFieldInitializer(Expression expr) { + private Expression transformFieldInitializer(Expression expr) { if (expr instanceof LocalVarOperator) { if (!(expr instanceof LocalLoadOperator)) { if ((GlobalOptions.debuggingFlags @@ -608,11 +620,16 @@ public class TransformConstructors { return expr; } + /** + * Remove initializers of synthetic fields and + * This is called for non static constructors in the analyze pass, + * after the constructors are analyzed. + */ public void removeSynthInitializers() { if ((Options.options & Options.OPTION_CONTRAFO) == 0 || isStatic || type01Count == 0) return; - + if ((Options.options & Options.OPTION_ANON) != 0) checkAnonymousConstructor(); @@ -733,7 +750,7 @@ public class TransformConstructors { } - public int transformOneField(int lastField, StructuredBlock ib) { + private int transformOneField(int lastField, StructuredBlock ib) { if (!(ib instanceof InstructionBlock)) return -1; @@ -787,7 +804,7 @@ public class TransformConstructors { return field; } - public void transformBlockInitializer(StructuredBlock block) { + private void transformBlockInitializer(StructuredBlock block) { StructuredBlock start = null; StructuredBlock tail = null; int lastField = -1; @@ -804,7 +821,7 @@ public class TransformConstructors { clazzAnalyzer.addBlockInitializer(lastField + 1, block); } - public boolean checkBlockInitializer(InvokeOperator invoke) { + private boolean checkBlockInitializer(InvokeOperator invoke) { if (!invoke.isThis() || invoke.getFreeOperandCount() != 0) return false; @@ -829,11 +846,63 @@ public class TransformConstructors { return true; } - private void removeDefaultSuper() { + + /* Checks if superInvoke is the default super call. + */ + private boolean isDefaultSuper(InvokeOperator superInvoke) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_CONSTRS) != 0) - GlobalOptions.err.println("removeDefaultSuper of " - + clazzAnalyzer.getClazz()); + GlobalOptions.err.println("isDefaultSuper: "+superInvoke); + + ClassInfo superClazz = superInvoke.getClassInfo(); + Expression[] params = superInvoke.getSubExpressions(); + if (superClazz == null) + return false; + + if ((Options.options & Options.OPTION_INNER) != 0 + && superClazz.getOuterClass() != null + && !Modifier.isStatic(superClazz.getModifiers())) { + + /* Super class is an inner class. Check if the default outer + * instance is passed. + */ + if (params.length != 2) + return false; + + Expression superOuterExpr = params[1].simplify(); + if (superOuterExpr instanceof ThisOperator + && (((ThisOperator) superOuterExpr).getClassInfo() + == superClazz.getOuterClass())) { + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println(" isDefaultSuper success"); + return true; + } + return false; + } + + int outerValCount = 0; + ClassAnalyzer superClazzAna = superInvoke.getClassAnalyzer(); + if (superClazzAna != null) { + OuterValues superOV = superClazzAna.getOuterValues(); + if (superOV != null) + outerValCount = superOV.getCount(); + } + + /* Check if only this and the outer value parameters are + * transmitted. The analyze pass already made sure, that the + * outer value parameters are correct. + */ + if (params.length != outerValCount + 1) + return false; + + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_CONSTRS) != 0) + GlobalOptions.err.println(" isDefaultSuper success"); + return true; + } + + private void removeDefaultSuper() { /* Check if we can remove the super() call of type1 constructors. * This transforms a type1 constructor in a type0 constructor. */ @@ -841,11 +910,6 @@ public class TransformConstructors { MethodAnalyzer current = cons[i]; FlowBlock header = cons[i].getMethodHeader(); StructuredBlock body = header.block; - - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_CONSTRS) != 0) - GlobalOptions.err.println("constr "+i+": "+body); - InstructionBlock ib; if (body instanceof InstructionBlock) ib = (InstructionBlock) body; @@ -854,45 +918,14 @@ public class TransformConstructors { InvokeOperator superInvoke = (InvokeOperator) ib.getInstruction().simplify(); - ClassInfo superClazz = superInvoke.getClassInfo(); - int superParamCount = superInvoke.getSubExpressions().length - 1; - - 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 */ + if (isDefaultSuper(superInvoke)) { + ib.removeBlock(); + if (i > type0Count) { + cons[i] = cons[type0Count]; + cons[type0Count] = current; } + type0Count++; } - ib.removeBlock(); - if (i > type0Count) { - cons[i] = cons[type0Count]; - cons[type0Count] = current; - } - type0Count++; } } @@ -1010,23 +1043,17 @@ public class TransformConstructors { || cons.length == 0) return; - removeDefaultSuper(); removeInitializers(); checkJikesContinuation(); if (outerValues != null) { - /* Now tell all constructors the value of outerValues parameters - * and simplify them again. - */ + /* Now tell all constructors the value of outerValues parameters */ for (int i=0; i< cons.length; i++) { for (int j = 0; j < outerValues.getCount(); j++) cons[i].getParamInfo(j+1) .setExpression(outerValues.getValue(j)); - // if (outerValues.isJikesAnonymousConstructor()) { - // /*XXX???*/ - // } - cons[i].getMethodHeader().simplify(); } } + removeDefaultSuper(); } } diff --git a/jode/jode/flow/TryBlock.java b/jode/jode/flow/TryBlock.java index 1971781..b9bf712 100644 --- a/jode/jode/flow/TryBlock.java +++ b/jode/jode/flow/TryBlock.java @@ -169,8 +169,8 @@ public class TryBlock extends StructuredBlock { if (instr.isVoid() || instr.getFreeOperandCount() != 0 || !(instr instanceof InvokeOperator) || !(catchBlock.catchBlock instanceof ThrowBlock) - || !(catchBlock.exceptionType.equals - (Type.tClass("java.lang.CloneNotSupportedException")))) + || !(((ClassType) catchBlock.exceptionType).getClassName().equals + ("java.lang.CloneNotSupportedException"))) return false; InvokeOperator arrayClone = (InvokeOperator) instr; @@ -191,8 +191,9 @@ public class TryBlock extends StructuredBlock { InvokeOperator throwOp = (InvokeOperator) throwExpr; if (!throwOp.isConstructor() - || !(throwOp.getClassType() - .equals(Type.tClass("java.lang.InternalError"))) + || !(throwOp.getClassType() instanceof ClassType) + || !(((ClassType) throwOp.getClassType()).getClassName() + .equals("java.lang.InternalError")) || throwOp.getMethodType().getParameterTypes().length != 1) return false; diff --git a/jode/jode/jvm/CodeVerifier.java b/jode/jode/jvm/CodeVerifier.java index c008128..ef28192 100644 --- a/jode/jode/jvm/CodeVerifier.java +++ b/jode/jode/jvm/CodeVerifier.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.util.BitSet; ///#def COLLECTIONS java.util import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1302,7 +1303,7 @@ public class CodeVerifier implements Opcodes { Instruction instr = null; - Iterator iter = block.getInstructions().iterator(); + Iterator iter = Arrays.asList(block.getInstructions()).iterator(); while (iter.hasNext()) { instr = (Instruction) iter.next(); modelEffect(instr, info); diff --git a/jode/jode/jvm/Interpreter.java b/jode/jode/jvm/Interpreter.java index 718385c..b3d4315 100644 --- a/jode/jode/jvm/Interpreter.java +++ b/jode/jode/jvm/Interpreter.java @@ -107,7 +107,7 @@ public class Interpreter implements Opcodes { /* If block is over continue with the next block */ if (nextBlock == null) return Void.TYPE; - iter = nextBlock.getInstructions().iterator(); + iter = Arrays.asList(nextBlock.getInstructions()).iterator(); succs = nextBlock.getSuccs(); handlers = nextBlock.getCatchers(); nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; diff --git a/jode/jode/jvm/SyntheticAnalyzer.java b/jode/jode/jvm/SyntheticAnalyzer.java index 2575792..e8eed24 100644 --- a/jode/jode/jvm/SyntheticAnalyzer.java +++ b/jode/jode/jvm/SyntheticAnalyzer.java @@ -35,6 +35,7 @@ import jode.type.MethodType; import java.lang.reflect.Modifier; ///#def COLLECTIONS java.util +import java.util.Arrays; import java.util.Iterator; ///#enddef @@ -117,7 +118,7 @@ public class SyntheticAnalyzer implements Opcodes { Block startBlock = bb.getStartBlock(); Handler[] excHandlers = bb.getExceptionHandlers(); if (startBlock == null - || startBlock.getInstructions().size() != 3 + || startBlock.getInstructions().length != 3 || excHandlers.length != 1 || excHandlers[0].getStart() != startBlock || excHandlers[0].getEnd() != startBlock @@ -126,8 +127,7 @@ public class SyntheticAnalyzer implements Opcodes { return false; for (int i=0; i< 3; i++) { - Instruction instr = - (Instruction) startBlock.getInstructions().get(i); + Instruction instr = startBlock.getInstructions()[i]; if (instr.getOpcode() != getClassOpcodes[i]) return false; if (getClassRefs[i] != null @@ -138,12 +138,11 @@ public class SyntheticAnalyzer implements Opcodes { } Block catchBlock = excHandlers[0].getCatcher(); - if (catchBlock.getInstructions().size() != 7) + if (catchBlock.getInstructions().length != 7) return false; int excSlot = -1; for (int i=0; i< 7; i++) { - Instruction instr = (Instruction) - catchBlock.getInstructions().get(i); + Instruction instr = catchBlock.getInstructions()[i]; if (instr.getOpcode() != getClassOpcodes[3+i]) return false; if (getClassRefs[3+i] != null @@ -177,7 +176,7 @@ public class SyntheticAnalyzer implements Opcodes { if (succBlocks.length > 1 || (succBlocks.length == 1 && succBlocks[0] != null)) return false; - Iterator iter = startBlock.getInstructions().iterator(); + Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator(); if (!iter.hasNext()) return false; Instruction instr = (Instruction) iter.next(); @@ -245,7 +244,8 @@ public class SyntheticAnalyzer implements Opcodes { return false; MethodInfo refMethod = classInfo.findMethod(ref.getName(), ref.getType()); - MethodType refType = Type.tMethod(ref.getType()); + MethodType refType = Type.tMethod(classInfo.getClassPath(), + ref.getType()); if ((refMethod.getModifiers() & modifierMask) != (Modifier.PRIVATE | Modifier.STATIC) || refType.getParameterTypes().length != params) @@ -291,7 +291,7 @@ public class SyntheticAnalyzer implements Opcodes { if (succBlocks.length > 1 || (succBlocks.length == 1 && succBlocks[0] != null)) return false; - Iterator iter = startBlock.getInstructions().iterator(); + Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator(); if (!iter.hasNext()) return false; @@ -361,7 +361,8 @@ public class SyntheticAnalyzer implements Opcodes { return false; MethodInfo refMethod = classInfo.findMethod(ref.getName(), ref.getType()); - MethodType refType = Type.tMethod(ref.getType()); + MethodType refType = Type.tMethod(classInfo.getClassPath(), + ref.getType()); if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE || refType.getParameterTypes().length != params) return false; @@ -398,7 +399,7 @@ public class SyntheticAnalyzer implements Opcodes { Block[] succBlocks = startBlock.getSuccs(); if (succBlocks.length != 1 || succBlocks[0] != null) return false; - Iterator iter = startBlock.getInstructions().iterator(); + Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator(); if (!iter.hasNext()) return false; @@ -439,7 +440,8 @@ public class SyntheticAnalyzer implements Opcodes { return false; MethodInfo refMethod = classInfo.findMethod(ref.getName(), ref.getType()); - MethodType refType = Type.tMethod(ref.getType()); + MethodType refType = Type.tMethod(classInfo.getClassPath(), + ref.getType()); if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE || !refMethod.getName().equals("
- *
- * If this is the bottom boundary, this specifies, which class our
- * type must extend and which interfaces it must implement.
- *
- * If this is the top boundary, this gives all interfaces and classes
- * that may extend the type. I.e. at least one interface or class extends
- * the searched type.
- *
- * @author Jochen Hoenicke */
-public class ClassInterfacesType extends ReferenceType {
-
- ClassInfo clazz;
- ClassInfo ifaces[];
-
- public ClassInfo getClazz() {
- return clazz != null ? clazz
- : ClassInfo.forName("java.lang.Object");
- }
-
- public ClassInterfacesType(String clazzName) {
- this(ClassInfo.forName(clazzName));
- }
-
- public ClassInterfacesType(ClassInfo clazz) {
- super(TC_CLASS);
- try {
- clazz.load(ClassInfo.HIERARCHY);
- } catch (IOException ex) {
- clazz.guess(ClassInfo.HIERARCHY);
- }
- if (clazz.isInterface()) {
- this.clazz = null;
- ifaces = new ClassInfo[] { clazz };
- } else {
- this.clazz =
- (clazz.getName() == "java.lang.Object") ? null : clazz;
- ifaces = new ClassInfo[0];
- }
- }
-
- public ClassInterfacesType(ClassInfo clazz, ClassInfo[] ifaces) {
- super(TC_CLASS);
- this.clazz = clazz;
- this.ifaces = ifaces;
- }
-
- static ClassInterfacesType create(ClassInfo clazz, ClassInfo[] ifaces) {
- /* Make sure that every {java.lang.Object} equals tObject */
- if (ifaces.length == 0 && clazz == null)
- return tObject;
- if (ifaces.length == 0)
- return tClass(clazz);
- if (ifaces.length == 1 && clazz == null)
- return tClass(ifaces[0]);
- return new ClassInterfacesType(clazz, ifaces);
- }
-
- public Type getSubType() {
- if ((clazz == null && ifaces.length == 1)
- || ifaces.length == 0)
- return tRange(this, tNull);
-
- /* We don't implement the set of types, that are castable to some
- * of the given classes or interfaces.
- */
- throw new jode.AssertError
- ("getSubType called on set of classes and interfaces!");
- }
-
- public Type getHint() {
- if (ifaces.length == 0
- || (clazz == null && ifaces.length == 1))
- return this;
- if (clazz != null)
- return Type.tClass(clazz);
- else
- return Type.tClass(ifaces[0]);
- }
-
- public Type getCanonic() {
- if (ifaces.length == 0
- || (clazz == null && ifaces.length == 1))
- return this;
- if (clazz != null)
- return Type.tClass(clazz);
- else
- return Type.tClass(ifaces[0]);
- }
-
- /**
- * Create the type corresponding to the range from bottomType to
- * this. Checks if the given type range may be not empty. This
- * means, that bottom.clazz is extended by this.clazz and that all
- * interfaces in bottom are implemented by an interface or by
- * clazz.
- * @param bottom the start point of the range
- * @return the range type, or tError if range is empty.
- */
- public Type createRangeType(ReferenceType bottomType) {
- if (bottomType.typecode != TC_CLASS)
- return tError;
-
- ClassInterfacesType bottom = (ClassInterfacesType) bottomType;
-
- if (bottomType == tObject)
- return (this == tObject) ? tObject : tRange(tObject, this);
-
- try {
- if (bottom.clazz != null) {
- /* The searched type must be a class type.
- */
- if (!bottom.clazz.superClassOf(this.clazz))
- return tError;
-
- /* All interfaces must be implemented by this.clazz
- */
- for (int i=0; i < bottom.ifaces.length; i++) {
- if (!bottom.ifaces[i].implementedBy(this.clazz))
- return tError;
- }
-
- if (bottom.clazz == this.clazz
- && bottom.ifaces.length == 0)
- return bottom;
-
- if (this.ifaces.length != 0)
- return tRange(bottom, create(this.clazz, new ClassInfo[0]));
- return tRange(bottom, this);
-
- } else {
-
- /* Now bottom.clazz is null (or tObject), find all
- * classes/interfaces that implement all bottom.ifaces.
- */
-
- ClassInfo clazz = this.clazz;
- if (clazz != null) {
- for (int i=0; i < bottom.ifaces.length; i++) {
- if (!bottom.ifaces[i].implementedBy(clazz)) {
- clazz = null;
- break;
- }
- }
- }
-
- /* If bottom is a single interface and equals some top
- * interface, then bottom is the only possible type.
- */
- if (clazz == null && bottom.ifaces.length == 1) {
- for (int i=0; i< this.ifaces.length; i++) {
- if (this.ifaces[i] == bottom.ifaces[0])
- return bottom;
- }
- }
-
- ClassInfo[] ifaces = new ClassInfo[this.ifaces.length];
- int count = 0;
- big_loop:
- for (int j=0; j < this.ifaces.length; j++) {
- for (int i=0; i < bottom.ifaces.length; i++) {
- if (!bottom.ifaces[i].implementedBy(this.ifaces[j]))
- continue big_loop;
- }
- ifaces[count++] = (this.ifaces[j]);
- }
-
- if (clazz == null && count == 0) {
- /* There are no more possible interfaces or classes left.
- * This is a type error.
- */
- return tError;
- } else if (count < ifaces.length) {
- ClassInfo[] shortIfaces = new ClassInfo[count];
- System.arraycopy(ifaces, 0, shortIfaces, 0, count);
- ifaces = shortIfaces;
- } else if (clazz == this.clazz)
- return tRange(bottom, this);
- return tRange(bottom, create(clazz, ifaces));
- }
- } catch (IOException ex) {
- /*XXX : There is something better than tError*/
- return tError;
- }
- }
-
- /**
- * Returns the specialized type of this and type.
- * We have two classes and multiple interfaces. The result
- * should be the object that extends both objects
- * and the union of all interfaces.
- */
- public Type getSpecializedType(Type type) {
- int code = type.typecode;
- if (code == TC_RANGE) {
- type = ((RangeType) type).getBottom();
- code = type.typecode;
- }
- if (code == TC_NULL)
- return this;
- if (code == TC_ARRAY)
- return ((ArrayType) type).getSpecializedType(this);
- if (code != TC_CLASS)
- return tError;
-
- ClassInterfacesType other = (ClassInterfacesType) type;
- ClassInfo clazz;
-
- /* First determine the clazz, one of the two classes must be a sub
- * class of the other or null.
- */
-
- try {
- if (this.clazz == null)
- clazz = other.clazz;
- else if (other.clazz == null)
- clazz = this.clazz;
- else if (this.clazz.superClassOf(other.clazz))
- clazz = other.clazz;
- else if (other.clazz.superClassOf(this.clazz))
- clazz = this.clazz;
- else
- return tError;
- } catch (IOException ex) {
- /*XXX : There is something better than tError*/
- return tError;
- }
-
- /* Most times (99.9999999 %) one of the two classes is already
- * more specialized. Optimize for this case. (I know of one
- * class where at one intersection this doesn't succeed)
- */
- if (clazz == this.clazz
- && implementsAllIfaces(this.clazz, this.ifaces, other.ifaces))
- return this;
- else if (clazz == other.clazz
- && implementsAllIfaces(other.clazz, other.ifaces,
- this.ifaces))
- return other;
-
- try {
- /* The interfaces are simply the union of both interfaces set.
- * But we can simplify this, if an interface is implemented by
- * another or by the class, we can omit it.
- */
- Vector ifaces = new Vector();
- big_loop_this:
- for (int i=0; i< this.ifaces.length; i++) {
- ClassInfo iface = this.ifaces[i];
- if (clazz != null && iface.implementedBy(clazz)) {
- continue big_loop_this;
- }
- for (int j=0; j
+ *
+ * If this is the bottom boundary, this specifies, which class our
+ * type must extend and which interfaces it must implement.
+ *
+ * If this is the top boundary, this gives all interfaces and classes
+ * that may extend the type. I.e. at least one interface or class extends
+ * the searched type.
+ *
+ * @author Jochen Hoenicke */
+public class MultiClassType extends ReferenceType {
+
+ ClassType classes[];
+
+ private MultiClassType(ClassType[] classes) {
+ super(TC_CLASSIFACE);
+ this.classes = classes;
+ }
+
+ public static ReferenceType create(ClassType[] classes) {
+ if (classes.length == 0)
+ return tObject;
+ if (classes.length == 1)
+ return classes[0];
+ return new MultiClassType(classes);
+ }
+
+ public Type getSubType() {
+ /* We don't implement the set of types, that are castable to some
+ * of the given classes or interfaces.
+ */
+ throw new jode.AssertError
+ ("getSubType called on set of classes and interfaces!");
+ }
+
+ /**
+ * Returns true, iff this type implements all interfaces in type
+ * and extends all objects in type.
+ */
+ public boolean isSubTypeOf(Type type) {
+ for (int i = 0; i < classes.length; i++)
+ if (!classes[i].isSubTypeOf(type))
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns true, iff this type implements all interfaces in type
+ * and extends all objects in type.
+ */
+ public boolean maybeSubTypeOf(Type type) {
+ for (int i = 0; i < classes.length; i++)
+ if (!classes[i].maybeSubTypeOf(type))
+ return false;
+ return true;
+ }
+
+ public Type getHint() {
+ return getCanonic();
+ }
+
+ public Type getCanonic() {
+ return classes[0];
+ }
+
+ /**
+ * Create the type corresponding to the range from bottomType to
+ * this. This removes all classes that doesn't extend all classes
+ * in bottom. If no class remains, this is a type error.
+ * @param bottom the start point of the range
+ * @return the range type, or tError if range is empty.
+ */
+ public Type createRangeType(ReferenceType bottomType) {
+ ReferenceType topType;
+ /**
+ * Check if we fully implement the bottom type.
+ */
+ int j;
+ for (j=0; j < classes.length; j++) {
+ if (!bottomType.maybeSubTypeOf(classes[j]))
+ break;
+ }
+
+ if (j == classes.length)
+ topType = this;
+ else {
+ /* Now we have at least one class, that doesn't implement
+ * bottomType, remove all such classes.
+ */
+ ClassType[] topClasses = new ClassType[classes.length - 1];
+ System.arraycopy(classes, 0, topClasses, 0, j);
+ int count = j;
+ for (j++; j < classes.length; j++) {
+ if (bottomType.isSubTypeOf(classes[j]))
+ topClasses[count++] = classes[j];
+ }
+
+ if (count == 0)
+ return tError;
+ if (count < topClasses.length - 1) {
+ ClassType[] shortClasses = new ClassType[count];
+ System.arraycopy(topClasses, 0, shortClasses, 0, count);
+ topClasses = shortClasses;
+ }
+ topType = create(topClasses);
+ }
+ if (topType.isSubTypeOf(bottomType))
+ /* This means that topType contains only classes that are also
+ * in bottomType. So topType is the whole range.
+ */
+ return topType;
+ return tRange(bottomType, topType);
+ }
+
+ boolean containsSuperTypeOf(Type type) {
+ for (int i = 0; i < classes.length; i++)
+ if (type.isSubTypeOf(classes[i]))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns the specialized type of this and type.
+ * We simple unify the lists of classes, but simplify them, to remove
+ * all classes that are already subtypes of some other class in the
+ * other list.
+ */
+ public Type getSpecializedType(Type type) {
+ if (type instanceof RangeType)
+ type = ((RangeType) type).getBottom();
+
+ /* Most times (almost always) one of the two types is
+ * already more specialized. Optimize for this case.
+ */
+ if (type.isSubTypeOf(this))
+ return this;
+ if (this.isSubTypeOf(type))
+ return type;
+
+ ClassType[] otherClasses;
+ if (type instanceof MultiClassType) {
+ otherClasses = ((MultiClassType) type).classes;
+ } else if (type instanceof ClassType) {
+ otherClasses = new ClassType[] { (ClassType) type };
+ } else
+ return tError;
+
+ /* The classes are simply the union of both classes set. But
+ * we can simplify this, if a class is implemented by another
+ * class in the other list, we can omit it.
+ */
+ Vector destClasses = new Vector();
+ big_loop_this:
+ for (int i=0; i< classes.length; i++) {
+ ClassType clazz = classes[i];
+ if (!clazz.isSubTypeOf(type)) {
+ /* This interface is not implemented by any of the other
+ * classes. Add it to the destClasses.
+ */
+ destClasses.addElement(clazz);
+ }
+ }
+ big_loop_other:
+ for (int i=0; i< otherClasses.length; i++) {
+ ClassType clazz = otherClasses[i];
+ if (!clazz.isSubTypeOf(this)) {
+ /* This interface is not implemented by any of the other
+ * classes. Add it to the destClasses.
+ */
+ destClasses.addElement(clazz);
+ }
+ }
+
+ ClassType[] classArray = new ClassType[destClasses.size()];
+ destClasses.copyInto(classArray);
+ return create(classArray);
+ }
+
+ /**
+ * Returns the generalized type of this and type. We have two
+ * classes and multiple interfaces. The result should be the
+ * object that is the the super class of both objects and all
+ * interfaces, that one class or interface of each type
+ * implements.
+ */
+ public Type getGeneralizedType(Type type) {
+ if (type instanceof RangeType)
+ type = ((RangeType) type).getTop();
+
+ /* Often one of the two classes is already more generalized.
+ * Optimize for this case.
+ */
+ if (type.isSubTypeOf(this))
+ return type;
+ if (this.isSubTypeOf(type))
+ return this;
+
+ if (!(type instanceof ReferenceType))
+ return tError;
+
+ Stack classTypes = new Stack();
+ for (int i = 0; i < classes.length; i++)
+ classTypes.push(classes[i]);
+ return ((ReferenceType)type).findCommonClassTypes(classTypes);
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer("{");
+ String comma = "";
+ for (int i=0; i< classes.length; i++) {
+ sb.append(comma).append(classes[i]);
+ comma = ", ";
+ }
+ return sb.append("}").toString();
+ }
+
+ public String getTypeSignature() {
+ return getCanonic().getTypeSignature();
+ }
+
+ public Class getTypeClass() throws ClassNotFoundException {
+ return getCanonic().getTypeClass();
+ }
+
+ /**
+ * Checks if we need to cast to a middle type, before we can cast from
+ * fromType to this type.
+ * @return the middle type, or null if it is not necessary.
+ */
+ public Type getCastHelper(Type fromType) {
+ return getCanonic().getCastHelper(fromType);
+ }
+
+ /**
+ * Checks if this type represents a valid type instead of a list
+ * of minimum types.
+ */
+ public boolean isValidType() {
+ return false;
+ }
+
+ /**
+ * Checks if this is a class or array type (but not a null type).
+ * @XXX remove this?
+ * @return true if this is a class or array type.
+ */
+ public boolean isClassType() {
+ return true;
+ }
+
+ /**
+ * Generates the default name, that is the `natural' choice for
+ * local of this type.
+ * @return the default name of a local of this type.
+ */
+ public String getDefaultName() {
+ return getCanonic().getDefaultName();
+ }
+
+ public int hashCode() {
+ int hash = 0;
+ for (int i=0; i < classes.length; i++) {
+ hash ^= classes[i].hashCode();
+ }
+ return hash;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof MultiClassType) {
+ MultiClassType type = (MultiClassType) o;
+ if (type.classes.length == classes.length) {
+ big_loop:
+ for (int i=0; i< type.classes.length; i++) {
+ for (int j=0; j
+ * types are NullType, MultiClassType, and ClassType with its sub types
+ * ClassInfoType, SystemClassType, and ArrayType.
*
* To do intersection on range types, the reference types need three
* more operations: specialization, generalization and
@@ -56,6 +60,7 @@ public abstract class ReferenceType extends Type {
* @param type the other type.
* @return the specialized type. */
public abstract Type getSpecializedType(Type type);
+
/**
* Returns the generalized type set of this and type. The result
* should be a type set, so that every type, is extended/implemented
@@ -65,6 +70,49 @@ public abstract class ReferenceType extends Type {
* @return the generalized type
*/
public abstract Type getGeneralizedType(Type type);
+
+ public Type findCommonClassTypes(Stack otherTypes) {
+ /* Consider each class and interface implemented by this.
+ * If any clazz or interface in other implements it, add it to
+ * the classes vector. Otherwise consider all sub interfaces.
+ */
+ Vector classes = new Vector();
+
+ type_loop:
+ while (!otherTypes.isEmpty()) {
+ ClassType type = (ClassType) otherTypes.pop();
+ if (type.equals(tObject))
+ /* tObject is always implied. */
+ continue type_loop;
+
+ for (Enumeration enum = classes.elements();
+ enum.hasMoreElements(); ) {
+ if (type.isSubTypeOf((Type) enum.nextElement()))
+ /* We can skip this, as another class already
+ * implies it. */
+ continue type_loop;
+ }
+
+ if (type.isSubTypeOf(this)) {
+ classes.addElement(type);
+ continue type_loop;
+ }
+
+ /* This clazz/interface is not implemented by this object.
+ * Try its parents now.
+ */
+ ClassType ifaces[] = type.getInterfaces();
+ for (int i=0; i < ifaces.length; i++)
+ otherTypes.push(ifaces[i]);
+ ClassType superClass = type.getSuperClass();
+ if (superClass != null)
+ otherTypes.push(superClass);
+ }
+ ClassType[] classArray = new ClassType[classes.size()];
+ classes.copyInto(classArray);
+ return MultiClassType.create(classArray);
+ }
+
/**
* Creates a range type set of this and bottom. The resulting type set
* contains all types, that extend all types in bottom and are extended
diff --git a/jode/jode/type/SystemClassType.java b/jode/jode/type/SystemClassType.java
new file mode 100644
index 0000000..f0a8f74
--- /dev/null
+++ b/jode/jode/type/SystemClassType.java
@@ -0,0 +1,72 @@
+/* SystemClassType 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.type;
+import jode.bytecode.ClassInfo;
+import java.util.Vector;
+import java.util.Stack;
+import java.util.Hashtable;
+import java.io.IOException;
+
+/**
+ * This class represents the type of a system class, i.e. the classes
+ * from package java.lang, that need special handling, like
+ * Object, String, StringBuffer, etc.
+ *
+ * @author Jochen Hoenicke
+ */
+public class SystemClassType extends ClassType {
+ ClassType superType;
+ ClassType[] ifacesTypes;
+ boolean isFinal, isInterface;
+
+ /**
+ * @param className The name of this system class, must be interned.
+ */
+ public SystemClassType(String className,
+ ClassType superType,
+ ClassType[] ifacesTypes,
+ boolean isFinal, boolean isInterface) {
+ super(TC_SYSCLASS, className);
+ this.superType = superType;
+ this.ifacesTypes = ifacesTypes;
+ this.isFinal = isFinal;
+ this.isInterface = isInterface;
+ }
+
+ public boolean isInterface() {
+ return isInterface;
+ }
+
+ public boolean isFinal() {
+ return isFinal;
+ }
+
+ public boolean isUnknown() {
+ return false;
+ }
+
+ public ClassType getSuperClass() {
+ return superType;
+ }
+
+ public ClassType[] getInterfaces() {
+ return ifacesTypes;
+ }
+}
diff --git a/jode/jode/type/Type.java b/jode/jode/type/Type.java
index 811a4e6..334b8ef 100644
--- a/jode/jode/type/Type.java
+++ b/jode/jode/type/Type.java
@@ -20,6 +20,7 @@
package jode.type;
import jode.AssertError;
import jode.GlobalOptions;
+import jode.bytecode.ClassPath;
import jode.bytecode.ClassInfo;
import jode.util.UnifyHash;
@@ -54,6 +55,8 @@ public class Type {
public static final int TC_UNKNOWN = 101;
public static final int TC_RANGE = 103;
public static final int TC_INTEGER = 107;
+ public static final int TC_SYSCLASS = 108;
+ public static final int TC_CLASSIFACE = 109;
private static final UnifyHash classHash = new UnifyHash();
private static final UnifyHash arrayHash = new UnifyHash();
@@ -131,12 +134,15 @@ public class Type {
*/
public static final Type tBoolByte= new IntegerType(IntegerType.IT_B
| IntegerType.IT_Z);
+
+ public final static ClassType[] EMPTY_IFACES = new ClassType[0];
/**
* This type represents the singleton set containing
* java.lang.Object
.
*/
- public static final ClassInterfacesType tObject =
- tClass("java.lang.Object");
+ public static final SystemClassType tObject =
+ tSystemClass("java.lang.Object",
+ null, EMPTY_IFACES, false, false);
/**
* This type represents the singleton set containing the special
* null type (the type of null).
@@ -147,21 +153,58 @@ public class Type {
* class types, array types, interface types and the null type.
*/
public static final Type tUObject = tRange(tObject, tNull);
+
+ /**
+ * This type represents the singleton set containing
+ * java.lang.Comparable
.
+ */
+ public static final SystemClassType tSerializable =
+ tSystemClass("java.io.Serializable",
+ null, EMPTY_IFACES, false, true);
+ /**
+ * This type represents the singleton set containing
+ * java.lang.Comparable
.
+ */
+ public static final SystemClassType tCloneable =
+ tSystemClass("java.lang.Cloneable",
+ null, EMPTY_IFACES, false, true);
+ /**
+ * This type represents the singleton set containing
+ * java.lang.Comparable
.
+ */
+ public static final SystemClassType tComparable =
+ tSystemClass("java.lang.Comparable",
+ null, EMPTY_IFACES, false, true);
+
/**
* This type represents the singleton set containing
* java.lang.String
.
*/
- public static final Type tString = tClass("java.lang.String");
+ public static final SystemClassType tString =
+ tSystemClass("java.lang.String",
+ tObject,
+ new ClassType[] { tSerializable, tComparable },
+ true, false);
/**
* This type represents the singleton set containing
* java.lang.StringBuffer
.
*/
- public static final Type tStringBuffer = tClass("java.lang.StringBuffer");
+ public static final SystemClassType tStringBuffer =
+ tSystemClass("java.lang.StringBuffer",
+ tObject, new ClassType[] { tSerializable },
+ true, false);
/**
* This type represents the singleton set containing
* java.lang.Class
.
*/
- public static final Type tJavaLangClass = tClass("java.lang.Class");
+ public static final SystemClassType tJavaLangClass =
+ tSystemClass("java.lang.Class",
+ tObject, new ClassType[] { tSerializable },
+ true, false);
+
+ static final ClassType[] arrayIfaces = {
+ tCloneable, tSerializable
+ };
/**
* Generate the singleton set of the type represented by the given
@@ -169,7 +212,7 @@ public class Type {
* @param type the type signature (or method signature).
* @return a singleton set containing the given type.
*/
- public static final Type tType(String type) {
+ public static final Type tType(ClassPath cp, String type) {
if (type == null || type.length() == 0)
return tError;
switch(type.charAt(0)) {
@@ -192,14 +235,14 @@ public class Type {
case 'V':
return tVoid;
case '[':
- return tArray(tType(type.substring(1)));
+ return tArray(tType(cp, type.substring(1)));
case 'L':
int index = type.indexOf(';');
if (index != type.length()-1)
return tError;
- return tClass(type.substring(1, index));
+ return tClass(cp, type.substring(1, index));
case '(':
- return tMethod(type);
+ return tMethod(cp, type);
}
throw new AssertError("Unknown type signature: "+type);
}
@@ -210,10 +253,24 @@ 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('/','.')));
+ public static final ClassType tClass(ClassPath classPath,
+ String clazzname) {
+ return tClass(classPath.getClassInfo(clazzname.replace('/','.')));
+ }
+
+ /**
+ * Generate the singleton set of the type represented by the given
+ * class name.
+ * @param clazzname the interned full qualified name of the class.
+ * The packages mus be separated by `.'.
+ * @return a singleton set containing the given type.
+ */
+ public static final SystemClassType tSystemClass
+ (String clazzName, ClassType superClass, ClassType[] ifaces,
+ boolean isFinal, boolean isInterface) {
+ return new SystemClassType(clazzName, superClass, ifaces,
+ isFinal, isInterface);
}
/**
@@ -222,15 +279,15 @@ public class Type {
* @param clazzinfo the jode.bytecode.ClassInfo.
* @return a singleton set containing the given type.
*/
- public static final ClassInterfacesType tClass(ClassInfo clazzinfo) {
+ public static final ClassType tClass(ClassInfo clazzinfo) {
int hash = clazzinfo.hashCode();
Iterator iter = classHash.iterateHashCode(hash);
while (iter.hasNext()) {
- ClassInterfacesType type = (ClassInterfacesType) iter.next();
+ ClassInfoType type = (ClassInfoType) iter.next();
if (type.getClassInfo() == clazzinfo)
return type;
}
- ClassInterfacesType type = new ClassInterfacesType(clazzinfo);
+ ClassInfoType type = new ClassInfoType(clazzinfo);
classHash.put(hash, type);
return type;
}
@@ -262,15 +319,16 @@ public class Type {
* @param signature the method decriptor.
* @return a method type (a singleton set).
*/
- public static MethodType tMethod(String signature) {
- int hash = signature.hashCode();
+ public static MethodType tMethod(ClassPath cp, String signature) {
+ int hash = signature.hashCode() + cp.hashCode();
Iterator iter = methodHash.iterateHashCode(hash);
while (iter.hasNext()) {
MethodType methodType = (MethodType) iter.next();
- if (methodType.getTypeSignature().equals(signature))
+ if (methodType.getTypeSignature().equals(signature)
+ && methodType.getClassPath().equals(cp))
return methodType;
}
- MethodType methodType = new MethodType(signature);
+ MethodType methodType = new MethodType(cp, signature);
methodHash.put(hash, methodType);
return methodType;
}
@@ -403,6 +461,20 @@ public class Type {
}
}
+ /**
+ * Returns true, if this is a sub type of type.
+ */
+ public boolean isSubTypeOf(Type type) {
+ return this == type;
+ }
+
+ /**
+ * Returns true, if this is a sub type of type.
+ */
+ public boolean maybeSubTypeOf(Type type) {
+ return isSubTypeOf(type);
+ }
+
/**
* Intersect this set of types with another type set and return the
* intersection.