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

"+ "write decompiled files to disk into directory destdir."); - err.println(" -s, --style {sun|gnu} "+ - "specify indentation style"); - err.println(" -i, --import ,"); - err.println(" "+ - "import classes used more than clslimit times"); - err.println(" "+ - "and packages with more then pkglimit used classes."); - err.println(" "+ - "Limit 0 means, never import, default is 0,1."); - - err.println("The following options can be turned on or off with `yes' or `no' argument."); - err.println(" --inner "+ - "decompile inner classes (default)."); - err.println(" --anonymous "+ - "decompile anonymous classes (default)."); - err.println(" --contrafo "+ - "transform constructors of inner classes (default)."); - err.println(" --lvt "+ - "use the local variable table (default)."); - err.println(" --pretty "+ - "use `pretty' names for local variables."); - err.println(" --push "+ - "allow PUSH instructions in output."); - err.println(" --decrypt "+ - "decrypt encrypted strings (default)."); - err.println(" --onetime "+ - "remove locals, that are used only one time."); - err.println(" --immediate "+ - "output source immediately (may produce buggy code)."); - err.println(" --verify "+ - "verify code before decompiling it."); - err.println("Debugging options, mainly used to debug this decompiler:"); - err.println(" -D, --debug=... "+ - "use --debug=help for more information."); } public static boolean handleOption(int option, int longind, String arg) { @@ -197,19 +163,6 @@ public class Main extends Options { errorInParams |= !GlobalOptions.setDebugging(arg); break; } - case 's': { - String arg = g.getOptarg(); - if ("sun".startsWith(arg)) - outputStyle = SUN_STYLE; - else if ("gnu".startsWith(arg)) - outputStyle = GNU_STYLE; - else { - GlobalOptions.err.println - ("jode.decompiler.Main: Unknown style `"+arg+"'."); - errorInParams = true; - } - break; - } case 'i': { String arg = g.getOptarg(); int comma = arg.indexOf(','); @@ -311,6 +264,12 @@ public class Main extends Options { writer.close(); /* Now is a good time to clean up */ System.gc(); + } catch (FileNotFoundException ex) { + GlobalOptions.err.println + ("Can't read "+ex.getMessage()+"."); + GlobalOptions.err.println + ("Check the class path ("+classPathStr+ + ") and check that you use the java class name."); } catch (IOException ex) { GlobalOptions.err.println ("Can't write source of "+params[i]+"."); diff --git a/jode/jode/decompiler/MethodAnalyzer.java b/jode/jode/decompiler/MethodAnalyzer.java index 6f2050b..4594487 100644 --- a/jode/jode/decompiler/MethodAnalyzer.java +++ b/jode/jode/decompiler/MethodAnalyzer.java @@ -195,7 +195,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { this.imports = imports; this.minfo = minfo; this.methodName = minfo.getName(); - this.methodType = Type.tMethod(minfo.getType()); + this.methodType = Type.tMethod(cla.getClassPath(), minfo.getType()); this.isConstructor = methodName.equals("") || methodName.equals(""); @@ -209,7 +209,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { int excCount = excattr.length; this.exceptions = new Type[excCount]; for (int i=0; i< excCount; i++) - exceptions[i] = Type.tClass(excattr[i]); + exceptions[i] = Type.tClass(classAnalyzer.getClassPath(), + excattr[i]); } if (minfo.isSynthetic() || methodName.indexOf('$') != -1) synth = new SyntheticAnalyzer(cla.getClazz(), minfo, true); @@ -403,15 +404,15 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { /** * Create a local info for a local variable located at an * instruction with the given address. - * @param addr the address of the instruction using this local. - * the address of the next instruction for stores. - * @param slot the slot, the local variable uses. + * @param lvi the local variable info of the bytecode package. * @return a new local info representing that local. */ public LocalInfo getLocalInfo(LocalVariableInfo lvi) { LocalInfo li = new LocalInfo(this, lvi.getSlot()); - if (lvi.getName() != null) - li.addHint(lvi.getName(), Type.tType(lvi.getType())); + if ((Options.options & Options.OPTION_LVT) != 0 + && lvi.getName() != null) + li.addHint(lvi.getName(), Type.tType(classAnalyzer.getClassPath(), + lvi.getType())); allLocals.addElement(li); return li; } @@ -427,7 +428,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { int count = 0; Block[] blocks = bb.getBlocks(); for (int i=0; i < blocks.length; i++) - count += blocks[i].getInstructions().size(); + count += blocks[i].getInstructions().length; return (double) count; } } @@ -462,10 +463,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { int count = 0; for (int i=0; i < blocks.length; i++) { int mark = 100; - int last = blocks[i].getInstructions().size() - 1; + int last = blocks[i].getInstructions().length - 1; for (int j=0; j <= last; j++) { - Instruction instr - = (Instruction) blocks[i].getInstructions().get(j); + Instruction instr = blocks[i].getInstructions()[j]; if (GlobalOptions.verboseLevel > 0 && j > mark) { GlobalOptions.err.print('.'); mark += 100; @@ -494,6 +494,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { methodHeader = new FlowBlock(this, 0, null); else methodHeader = flows[startBlock.getBlockNr()]; + methodHeader.addStartPred(); Handler[] handlers = bb.getExceptionHandlers(); excHandlers = new TransformExceptionHandlers(flows); @@ -504,7 +505,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { FlowBlock handler = flows[handlers[i].getCatcher().getBlockNr()]; if (handlers[i].getType() != null) - type = Type.tClass(handlers[i].getType()); + type = Type.tClass(classAnalyzer.getClassPath(), + handlers[i].getType()); excHandlers.addHandler(start, end, handler, type); } @@ -515,6 +517,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { excHandlers.analyze(); methodHeader.analyze(); + methodHeader.removeStartPred(); if ((Options.options & Options.OPTION_PUSH) == 0 && methodHeader.mapStackToLocal()) @@ -537,7 +540,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { * code of this method, but not the method scoped classes. */ public void analyze(ProgressListener pl, double done, double scale) - throws ClassFormatError + throws ClassFormatError { if (pl != null) pl.updateProgress(done, methodName); @@ -546,7 +549,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { CodeVerifier verifier = new CodeVerifier(getClazz(), minfo, bb); try { - verifier.verify(); + verifier.verify(); } catch (VerifyException ex) { ex.printStackTrace(GlobalOptions.err); throw new jode.AssertError("Verification error"); @@ -684,6 +687,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { boolean declareAsConstructor = isConstructor; int skipParams = 0; + int modifiedModifiers = minfo.getModifiers(); if (isConstructor() && !isStatic() && classAnalyzer.outerValues != null) skipParams = classAnalyzer.outerValues.getCount(); @@ -693,6 +697,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { declareAsConstructor = true; skipParams = hasJikesOuterValue && classAnalyzer.outerValues.getCount() > 0 ? 1 : 0; + // get the modifiers of the real constructor + modifiedModifiers = jikesConstructor.minfo.getModifiers(); } if (isJikesBlockInitializer) @@ -710,11 +716,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { * But this rule doesn't necessarily apply for anonymous * classes... */ - && ((minfo.getModifiers() + && ((modifiedModifiers & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE | Modifier.SYNCHRONIZED | Modifier.STATIC | Modifier.ABSTRACT | Modifier.NATIVE)) - == (getClassAnalyzer().getModifiers() + == (classAnalyzer.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) || classAnalyzer.getName() == null) && classAnalyzer.constructors.length == 1) { @@ -745,9 +751,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { int modifiedModifiers = minfo.getModifiers(); if (isConstructor() && !isStatic() - && (Options.options & Options.OPTION_CONTRAFO) != 0 - && classAnalyzer.outerValues != null) - skipParams = classAnalyzer.outerValues.getCount(); + && (Options.options & Options.OPTION_CONTRAFO) != 0) { + if (classAnalyzer.outerValues != null) + skipParams = classAnalyzer.outerValues.getCount(); + else if (classAnalyzer.getOuterInstance() != null) + skipParams = 1; + } if (jikesConstructor != null) { // This is the real part of a jikes constructor @@ -790,6 +799,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer { if (isConstructor() && isStatic()) modifiedModifiers &= ~Modifier.FINAL; + writer.startOp(writer.NO_PAREN, 0); + writer.startOp(writer.NO_PAREN, 5); String delim =""; if (minfo.isSynthetic()) { @@ -814,24 +825,35 @@ 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); 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(")"); } + writer.endOp(); if (exceptions.length > 0) { - writer.println(""); - writer.print("throws "); + writer.breakOp(); + writer.print(" throws "); + writer.startOp(writer.NO_PAREN, 0); for (int i= 0; i< exceptions.length; i++) { - if (i > 0) + if (i > 0) { writer.print(", "); + writer.breakOp(); + } writer.printType(exceptions[i]); } + writer.endOp(); } + writer.endOp(); if (bb != null) { writer.openBrace(); writer.tab(); diff --git a/jode/jode/decompiler/Opcodes.java b/jode/jode/decompiler/Opcodes.java index 31e9f2c..63ef253 100644 --- a/jode/jode/decompiler/Opcodes.java +++ b/jode/jode/decompiler/Opcodes.java @@ -140,6 +140,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes { public static void addOpcode(FlowBlock flow, Instruction instr, MethodAnalyzer ma) { + ClassPath cp = ma.getClassAnalyzer().getClassPath(); int opcode = instr.getOpcode(); switch (opcode) { case opc_nop: @@ -373,7 +374,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes { break; } case opc_new: { - Type type = Type.tType(instr.getClazzType()); + Type type = Type.tType(cp, instr.getClazzType()); ma.useType(type); flow.appendBlock(createNormal(ma, instr, new NewOperator(type))); break; @@ -388,14 +389,14 @@ public abstract class Opcodes implements jode.bytecode.Opcodes { new ThrowBlock(new NopOperator(Type.tUObject)))); break; case opc_checkcast: { - Type type = Type.tType(instr.getClazzType()); + Type type = Type.tType(cp, instr.getClazzType()); ma.useType(type); flow.appendBlock(createNormal (ma, instr, new CheckCastOperator(type))); break; } case opc_instanceof: { - Type type = Type.tType(instr.getClazzType()); + Type type = Type.tType(cp, instr.getClazzType()); ma.useType(type); flow.appendBlock(createNormal (ma, instr, new InstanceOfOperator(type))); @@ -410,7 +411,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes { new MonitorExitOperator())); break; case opc_multianewarray: { - Type type = Type.tType(instr.getClazzType()); + Type type = Type.tType(cp, instr.getClazzType()); ma.useType(type); int dimension = instr.getDimensions(); flow.appendBlock(createNormal diff --git a/jode/jode/decompiler/Options.java b/jode/jode/decompiler/Options.java index 7db7694..811bbc4 100644 --- a/jode/jode/decompiler/Options.java +++ b/jode/jode/decompiler/Options.java @@ -22,11 +22,6 @@ import jode.bytecode.ClassInfo; import java.io.IOException; public class Options { - public static final int TAB_SIZE_MASK = 0x0f; - public static final int BRACE_AT_EOL = 0x10; - public static final int SUN_STYLE = 0x14; - public static final int GNU_STYLE = 0x02; - public static final int OPTION_LVT = 0x0001; public static final int OPTION_INNER = 0x0002; public static final int OPTION_ANON = 0x0004; @@ -42,8 +37,6 @@ public class Options { OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY | OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO; - public static int outputStyle = SUN_STYLE; - public final static boolean doAnonymous() { return (options & OPTION_ANON) != 0; } diff --git a/jode/jode/decompiler/OuterValues.java b/jode/jode/decompiler/OuterValues.java index 224cebc..503c25c 100644 --- a/jode/jode/decompiler/OuterValues.java +++ b/jode/jode/decompiler/OuterValues.java @@ -30,30 +30,30 @@ import java.util.Vector; import java.util.Enumeration; /** - * A list of local variables that a method scoped class inherits - * from its declaring method. + *

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 + *

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("") || unifyParam == -1 diff --git a/jode/jode/obfuscator/ClassBundle.java b/jode/jode/obfuscator/ClassBundle.java index 824ba7b..654baaf 100644 --- a/jode/jode/obfuscator/ClassBundle.java +++ b/jode/jode/obfuscator/ClassBundle.java @@ -25,6 +25,7 @@ import jode.bytecode.Reference; import jode.obfuscator.modules.WildCard; import jode.obfuscator.modules.MultiIdentifierMatcher; import jode.obfuscator.modules.SimpleAnalyzer; +import jode.obfuscator.modules.IdentityRenamer; import java.io.*; import java.util.zip.ZipOutputStream; @@ -427,27 +428,7 @@ public class ClassBundle implements OptionHandler { if (postTrafos == null) postTrafos = new CodeTransformer[0]; if (renamer == null) - renamer = new Renamer() { - public Iterator generateNames(Identifier ident) { - final String base = ident.getName(); - return new Iterator() { - int last = 0; - - public boolean hasNext() { - return true; - } - - public Object next() { - return (last++ == 0 ? base : base + last); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - }; - + renamer = new IdentityRenamer(); Runtime runtime = Runtime.getRuntime(); long free = runtime.freeMemory(); diff --git a/jode/jode/obfuscator/ClassIdentifier.java b/jode/jode/obfuscator/ClassIdentifier.java index dcb133f..6cca3e1 100644 --- a/jode/jode/obfuscator/ClassIdentifier.java +++ b/jode/jode/obfuscator/ClassIdentifier.java @@ -262,7 +262,7 @@ public class ClassIdentifier extends Identifier { /* add a field serializableVersionUID if not existent */ long serialVersion = calcSerialVersionUID(); FieldInfo UIDField = new FieldInfo - (info, "serialVersionUID", "J", + ("serialVersionUID", "J", Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); UIDField.setConstant(new Long(serialVersion)); FieldIdentifier UIDident = new FieldIdentifier(this, UIDField); @@ -452,6 +452,7 @@ public class ClassIdentifier extends Identifier { } if (extraClasses != null) { for (int i=0; i < extraClasses.length; i++) { + System.err.println("ec["+i+"]:"+extraClasses[i].getName()); Main.getClassBundle() .getClassIdentifier(extraClasses[i].getName()); } diff --git a/jode/jode/obfuscator/CodeAnalyzer.java b/jode/jode/obfuscator/CodeAnalyzer.java index 5b047c4..b0b2a8f 100644 --- a/jode/jode/obfuscator/CodeAnalyzer.java +++ b/jode/jode/obfuscator/CodeAnalyzer.java @@ -18,8 +18,8 @@ */ package jode.obfuscator; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; public interface CodeAnalyzer extends CodeTransformer { - public void analyzeCode(MethodIdentifier parent, BytecodeInfo bytecode); + public void analyzeCode(MethodIdentifier parent, BasicBlocks bb); } diff --git a/jode/jode/obfuscator/CodeTransformer.java b/jode/jode/obfuscator/CodeTransformer.java index f536545..d625c18 100644 --- a/jode/jode/obfuscator/CodeTransformer.java +++ b/jode/jode/obfuscator/CodeTransformer.java @@ -18,8 +18,8 @@ */ package jode.obfuscator; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; public interface CodeTransformer { - public void transformCode(BytecodeInfo bytecode); + public void transformCode(BasicBlocks bb); } diff --git a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java index 59d5430..bbd66f8 100644 --- a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java +++ b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java @@ -22,7 +22,7 @@ import jode.jvm.Interpreter; import jode.jvm.SimpleRuntimeEnvironment; import jode.jvm.InterpreterException; import jode.bytecode.Reference; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; import jode.bytecode.TypeSignature; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; @@ -276,9 +276,9 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { MethodIdentifier mi = (MethodIdentifier) Main.getClassBundle().getIdentifier(ref); if (mi != null) { - BytecodeInfo code = mi.info.getBytecode(); - if (code != null) - return interpreter.interpretMethod(code, cls, params); + BasicBlocks bb = mi.info.getBasicBlocks(); + if (bb != null) + return interpreter.interpretMethod(bb, cls, params); } throw new InterpreterException("Invoking library method " + ref + "."); } diff --git a/jode/jode/obfuscator/FieldIdentifier.java b/jode/jode/obfuscator/FieldIdentifier.java index 6cbd144..97bcbaf 100644 --- a/jode/jode/obfuscator/FieldIdentifier.java +++ b/jode/jode/obfuscator/FieldIdentifier.java @@ -123,6 +123,11 @@ public class FieldIdentifier extends Identifier{ fieldListeners.add(ident); } + public void removeFieldListener(Identifier ident) { + if (fieldListeners != null) + fieldListeners.remove(ident); + } + public void setNotConstant() { if (notConstant) return; diff --git a/jode/jode/obfuscator/MethodIdentifier.java b/jode/jode/obfuscator/MethodIdentifier.java index 37090d8..d9cdcd1 100644 --- a/jode/jode/obfuscator/MethodIdentifier.java +++ b/jode/jode/obfuscator/MethodIdentifier.java @@ -52,20 +52,31 @@ public class MethodIdentifier extends Identifier implements Opcodes { this.clazz = clazz; this.info = info; - BytecodeInfo bytecode = info.getBytecode(); - if (bytecode != null) { - if ((Main.stripping & Main.STRIP_LVT) != 0) - info.getBytecode().setLocalVariableTable(null); - if ((Main.stripping & Main.STRIP_LNT) != 0) - info.getBytecode().setLineNumberTable(null); + BasicBlocks bb = info.getBasicBlocks(); + if (bb != null) { + if ((Main.stripping & + (Main.STRIP_LVT | Main.STRIP_LNT)) != 0) { + Block[] blocks = bb.getBlocks(); + for (int i = 0; i < blocks.length; i++) { + Instruction[] instrs = blocks[i].getInstructions(); + for (int j = 0; j < instrs.length; j++) { + if ((Main.stripping & Main.STRIP_LVT) != 0 + && instrs[j].hasLocal()) + instrs[j].setLocalInfo + (LocalVariableInfo + .getInfo(instrs[j].getLocalSlot())); + if ((Main.stripping & Main.STRIP_LNT) != 0) + instrs[j].setLineNr(-1); + } + } + } codeAnalyzer = Main.getClassBundle().getCodeAnalyzer(); CodeTransformer[] trafos = Main.getClassBundle().getPreTransformers(); for (int i = 0; i < trafos.length; i++) { - trafos[i].transformCode(bytecode); + trafos[i].transformCode(bb); } - info.setBytecode(bytecode); } } @@ -98,9 +109,9 @@ public class MethodIdentifier extends Identifier implements Opcodes { .reachableClass(exceptions[i]); } - BytecodeInfo code = info.getBytecode(); - if (code != null) - codeAnalyzer.analyzeCode(this, code); + BasicBlocks bb = info.getBasicBlocks(); + if (bb != null) + codeAnalyzer.analyzeCode(this, bb); } public Identifier getParent() { @@ -169,63 +180,64 @@ public class MethodIdentifier extends Identifier implements Opcodes { ClassBundle bundle = Main.getClassBundle(); info.setType(bundle.getTypeAlias(type)); if (codeAnalyzer != null) { - BytecodeInfo bytecode = info.getBytecode(); + BasicBlocks bb = info.getBasicBlocks(); try { - codeAnalyzer.transformCode(bytecode); + codeAnalyzer.transformCode(bb); CodeTransformer[] trafos = bundle.getPostTransformers(); for (int i = 0; i < trafos.length; i++) { - trafos[i].transformCode(bytecode); + trafos[i].transformCode(bb); } } catch (RuntimeException ex) { ex.printStackTrace(GlobalOptions.err); - bytecode.dumpCode(GlobalOptions.err); + bb.dumpCode(GlobalOptions.err); } - for (Iterator iter = bytecode.getInstructions().iterator(); - iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - switch (instr.getOpcode()) { - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: - case opc_invokevirtual: { - instr.setReference - (Main.getClassBundle() - .getReferenceAlias(instr.getReference())); - break; - - } - case opc_putstatic: - case opc_putfield: - case opc_getstatic: - case opc_getfield: { - instr.setReference - (Main.getClassBundle() - .getReferenceAlias(instr.getReference())); - break; - } - case opc_new: - case opc_checkcast: - case opc_instanceof: - case opc_multianewarray: { - instr.setClazzType - (Main.getClassBundle() - .getTypeAlias(instr.getClazzType())); - break; - } + Block[] blocks = bb.getBlocks(); + for (int i = 0; i < blocks.length; i++) { + Instruction[] instrs = blocks[i].getInstructions(); + for (int j = 0; j < instrs.length; j++) { + switch (instrs[j].getOpcode()) { + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: { + instrs[j].setReference + (Main.getClassBundle() + .getReferenceAlias(instrs[j].getReference())); + break; + + } + case opc_putstatic: + case opc_putfield: + case opc_getstatic: + case opc_getfield: { + instrs[j].setReference + (Main.getClassBundle() + .getReferenceAlias(instrs[j].getReference())); + break; + } + case opc_new: + case opc_checkcast: + case opc_instanceof: + case opc_multianewarray: { + instrs[j].setClazzType + (Main.getClassBundle() + .getTypeAlias(instrs[j].getClazzType())); + break; + } + } } } - - Handler[] handlers = bytecode.getExceptionHandlers(); + + Handler[] handlers = bb.getExceptionHandlers(); for (int i=0; i< handlers.length; i++) { - if (handlers[i].type != null) { + if (handlers[i].getType() != null) { ClassIdentifier ci = Main.getClassBundle() - .getClassIdentifier(handlers[i].type); + .getClassIdentifier(handlers[i].getType()); if (ci != null) - handlers[i].type = ci.getFullAlias(); + handlers[i].setType(ci.getFullAlias()); } } - info.setBytecode(bytecode); } String[] exceptions = info.getExceptions(); diff --git a/jode/jode/obfuscator/modules/ConstantAnalyzer.java b/jode/jode/obfuscator/modules/ConstantAnalyzer.java index 85f9cc8..c5672fa 100644 --- a/jode/jode/obfuscator/modules/ConstantAnalyzer.java +++ b/jode/jode/obfuscator/modules/ConstantAnalyzer.java @@ -24,13 +24,16 @@ import jode.GlobalOptions; import jode.bytecode.*; import jode.jvm.InterpreterException; import jode.obfuscator.*; +import jode.util.StringQuoter; import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.lang.reflect.InvocationTargetException; +import java.io.PrintWriter; import java.util.BitSet; ///#def COLLECTIONS java.util +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -45,12 +48,27 @@ import java.util.ListIterator; * Analyze the code, assuming every field that is not yet written to * is constant. This may imply that some code is dead code. * - * @author Jochen Hoenicke - */ + * While we analyze the Code we remember, which local variable and + * which stack slot is constant for each instruction and if the + * instruction is dead. First we assume that every local and every + * slot is constant for each instruction, and that all instructions + * are dead code. + * + * Now we mark all local variables of the first instruction as not + * constant and the first instruction as modified. + * + * While there is a modified instruction, we pick one and repeat the + * following algorithm: + * + * If the instruction produces a constant result (because it is a ldc + * instruction, or it combines constant values), we put that instruction + * on the ConstantListener-Queue of all constant inputs and put the + * constant result on the ConstantListener-Queue of that instruction. + * + * + * @author Jochen Hoenicke */ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { - BytecodeInfo bytecode; - private static ConstantRuntimeEnvironment runtime = new ConstantRuntimeEnvironment(); @@ -67,102 +85,44 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { private final static int CMP_EQUAL_MASK = (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ); - final static int CONSTANT = 0x02; - final static int CONSTANTFLOW = 0x04; - final static int RETASTORE = 0x08; - final static int RETURNINGJSR = 0x10; + final static int CONSTANT = 0x02; + final static int CONSTANTFLOW = 0x04; + + /** + * The blocks, that are not analyzed yet, but whose before is + * already set. + */ + TodoQueue modifiedQueue = new TodoQueue(); + + /** + * The basic blocks for the current method. + */ + BasicBlocks bb; + /** + * All block infos of all blocks in the current method. + */ + BlockInfo[] infos; + /** + * The currently analyzed method, only valid while analyzeCode is running. + */ + MethodIdentifier methodIdent; + Map fieldDependencies; + + Map bbInfos = new HashMap(); + Map constantInfos = new HashMap(); private interface ConstantListener { public void constantChanged(); } - private final static class JSRTargetInfo implements Cloneable { - Instruction jsrTarget; - BitSet usedLocals; - - /** - * The dependent entries, that want to know if the bit set changed. - * This is either a StackLocalInfo (the ret info) or a single - * JSRTargetInfo or a Collection of JSRTargetInfos. - */ - Object dependent; - - public JSRTargetInfo(Instruction target) { - jsrTarget = target; - usedLocals = new BitSet(); - } - - public JSRTargetInfo copy() { - try { - JSRTargetInfo result = (JSRTargetInfo) clone(); - result.usedLocals = (BitSet) usedLocals.clone(); - addDependent(result); - return result; - } catch (CloneNotSupportedException ex) { - throw new IncompatibleClassChangeError(ex.getMessage()); - } - } - - private void addDependent(JSRTargetInfo result) { - if (dependent == null || dependent == result) - dependent = result; - else if (dependent instanceof JSRTargetInfo) { - Set newDeps = new HashSet(); - newDeps.add(dependent); - newDeps.add(result); - } else if (dependent instanceof Collection) { - ((Collection) dependent).add(result); - } - } - - public void setRetInfo(StackLocalInfo retInfo) { - dependent = retInfo; - } - - public boolean uses(int localSlot) { - return usedLocals.get(localSlot); - } - - public void addUsed(int localSlot) { - if (usedLocals.get(localSlot)) - return; - usedLocals.set(localSlot); - - if (dependent instanceof StackLocalInfo) - ((StackLocalInfo) dependent).enqueue(); - else if (dependent instanceof JSRTargetInfo) - ((JSRTargetInfo) dependent).addUsed(localSlot); - else if (dependent instanceof Collection) { - Iterator iter = ((Collection) dependent).iterator(); - while (iter.hasNext()) { - JSRTargetInfo dep = (JSRTargetInfo) iter.next(); - dep.addUsed(localSlot); - } - } - } - - public void merge(JSRTargetInfo o) { - o.addDependent(this); - for (int slot = 0; slot < o.usedLocals.size(); slot++) { - if (o.usedLocals.get(slot)) - addUsed(slot); - } - } - - public String toString() { - StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget)); - if (dependent instanceof StackLocalInfo) - sb.append("->").append(((StackLocalInfo) dependent).instr); - return sb.append(usedLocals) - .append('_').append(hashCode()).toString(); - } - } - private static class ConstValue implements ConstantListener { public final static Object VOLATILE = new Object(); /** * The constant value, VOLATILE if value is not constant. - * This may also be an instance of JSRTargetInfo + * + * This may also be an instance of BlockInfo and point + * to the target block of the jsr, for which this variable + * contains the return value. */ Object value; /** @@ -182,23 +142,41 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { listeners = new HashSet(); } + public ConstValue(int stackSize) { + this.value = VOLATILE; + this.stackSize = stackSize; + } + public ConstValue(ConstValue constant) { value = constant.value; stackSize = constant.stackSize; listeners = new HashSet(); constant.addConstantListener(this); } - - public ConstValue(int stackSize) { - this.value = VOLATILE; - this.stackSize = stackSize; - } - + public ConstValue copy() { - return (value == VOLATILE) ? this + return value == VOLATILE ? this : new ConstValue(this); } + /** + * Merge the other value into this value. + */ + public void merge(ConstValue other) { + if (this == other) + return; + + if (value == null ? other.value == null + : value.equals(other.value)) { + if (other.value != VOLATILE) + other.addConstantListener(this); + return; + } + + if (value != VOLATILE) + fireChanged(); + } + public void addConstantListener(ConstantListener l) { listeners.add(l); } @@ -209,8 +187,11 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { public void fireChanged() { value = VOLATILE; - for (Iterator i = listeners.iterator(); i.hasNext(); ) - ((ConstantListener) i.next()).constantChanged(); + for (Iterator i = listeners.iterator(); i.hasNext(); ) { + ConstantListener l = (ConstantListener) i.next(); + System.err.println(" notifying: "+ l); + l.constantChanged(); + } listeners = null; } @@ -219,87 +200,174 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { fireChanged(); } + public String toString() { + String result; + if (value == VOLATILE) + result = "vol("+stackSize+")"; + else if (value instanceof String) + result = StringQuoter.quote((String) value); + else + result = String.valueOf(value); + +// StringBuffer sb = new StringBuffer(result).append('{'); +// Iterator i = listeners.iterator(); +// while (i.hasNext()) +// sb.append(i.next()).append(','); +// result = sb.append('}').toString(); + return result+"@"+hashCode(); + } + } + + private final static class JsrInfo implements Cloneable { + BitSet usedLocals; + BlockInfo jsrTarget; + Collection callers; + /** - * Merge the other value into this value. + * The dependent entries, that want to know if the bit set changed. + * This is either a StackLocalInfo (the ret info) or a single + * JsrInfo or a Collection of JsrInfos. */ - public void merge(ConstValue other) { - if (this == other) - return; + Object dependent; - if (value == null ? other.value == null - : value.equals(other.value)) { - if (value != VOLATILE) { - other.addConstantListener(this); - this.addConstantListener(other); - } + public JsrInfo(BlockInfo target) { + jsrTarget = target; + callers = new ArrayList(); + usedLocals = new BitSet(); + } + + public JsrInfo copy() { + try { + JsrInfo result = (JsrInfo) clone(); + result.usedLocals = (BitSet) usedLocals.clone(); + addDependent(result); + return result; + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError(ex.getMessage()); + } + } + + private void addDependent(JsrInfo result) { + if (dependent == null || dependent == result) + dependent = result; + else if (dependent instanceof JsrInfo) { + Set newDeps = new HashSet(); + newDeps.add(dependent); + newDeps.add(result); + } else if (dependent instanceof Collection) { + ((Collection) dependent).add(result); + } + } + + public boolean uses(int localSlot) { + return usedLocals.get(localSlot); + } + + public void setRetInfo(BlockInfo retInfo) { + dependent = retInfo; + for (Iterator i = callers.iterator(); i.hasNext(); ) + retInfo.mergeRetLocals(this, (BlockInfo)i.next()); + } + + public void addCaller(BlockInfo caller) { + if (callers.contains(caller)) return; + callers.add(caller); + + if (dependent instanceof BlockInfo) { + BlockInfo retInfo = ((BlockInfo) dependent); + for (Iterator i = callers.iterator(); i.hasNext(); ) + retInfo.mergeRetLocals(this, caller); + } else if (dependent instanceof JsrInfo) { + ((JsrInfo) dependent).addCaller(caller); + } else if (dependent instanceof Collection) { + Iterator iter = ((Collection) dependent).iterator(); + while (iter.hasNext()) { + JsrInfo dep = (JsrInfo) iter.next(); + dep.addCaller(caller); + } } + } + + public boolean hasRetInfo() { + if (dependent instanceof BlockInfo) + return true; + else if (dependent instanceof JsrInfo) + return ((JsrInfo) dependent).hasRetInfo(); + else if (dependent instanceof Collection) { + Iterator iter = ((Collection) dependent).iterator(); + while (iter.hasNext()) { + JsrInfo dep = (JsrInfo) iter.next(); + if (dep.hasRetInfo()) + return true; + } + } + return false; + } - if (value instanceof JSRTargetInfo - && other.value instanceof JSRTargetInfo - && (((JSRTargetInfo) value).jsrTarget - == ((JSRTargetInfo) other.value).jsrTarget)) { - ((JSRTargetInfo) value).merge((JSRTargetInfo) other.value); + public void addUsed(int localSlot) { + if (usedLocals.get(localSlot)) return; + usedLocals.set(localSlot); + + if (dependent instanceof BlockInfo) { + BlockInfo retInfo = ((BlockInfo) dependent); + for (Iterator i = callers.iterator(); i.hasNext(); ) + retInfo.mergeRetLocals(this, (BlockInfo)i.next()); + } else if (dependent instanceof JsrInfo) { + ((JsrInfo) dependent).addUsed(localSlot); + } else if (dependent instanceof Collection) { + Iterator iter = ((Collection) dependent).iterator(); + while (iter.hasNext()) { + JsrInfo dep = (JsrInfo) iter.next(); + dep.addUsed(localSlot); + } } + } - if (value != VOLATILE) - fireChanged(); -// if (other.value != VOLATILE) -// other.fireChanged(); + public void merge(JsrInfo o) { + o.addDependent(this); + for (int slot = 0; slot < o.usedLocals.size(); slot++) { + if (o.usedLocals.get(slot)) + addUsed(slot); + } + for (Iterator i = o.callers.iterator(); i.hasNext();) { + addCaller((BlockInfo) i.next()); + } } public String toString() { - return value == VOLATILE ? "vol("+stackSize+")" : ""+value; + StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget)); + if (dependent instanceof BlockInfo) + sb.append("->").append(((BlockInfo) dependent).nr); + return sb.append(usedLocals) + .append('_').append(hashCode()).toString(); } } - private static class TodoQueue { - StackLocalInfo first; - } - - private static class StackLocalInfo implements ConstantListener { + private static class StackLocalInfo { ConstValue[] stack; ConstValue[] locals; - Instruction instr; - ConstantInfo constInfo; - StackLocalInfo retInfo; - - StackLocalInfo nextOnQueue; - - /** - * The queue that should be notified, if the constant values of - * this instruction changes. We put ourself on this queue in that - * case. - */ - TodoQueue notifyQueue; - - public ConstValue copy(ConstValue value) { - return (value == null) ? null : value.copy(); - } + int stackDepth; private StackLocalInfo(ConstValue[] stack, - ConstValue[] locals, - TodoQueue notifyQueue) { + ConstValue[] locals, int stackDepth) { this.stack = stack; - this.locals = new ConstValue[locals.length]; - for (int i=0; i< locals.length; i++) - this.locals[i] = copy(locals[i]); - this.notifyQueue = notifyQueue; + this.locals = locals; + this.stackDepth = stackDepth; } - public StackLocalInfo(int numLocals, - boolean isStatic, String methodTypeSig, - TodoQueue notifyQueue) { + public StackLocalInfo(int maxStack, int numLocals, + boolean isStatic, String methodTypeSig) { String[] paramTypes = TypeSignature.getParameterTypes(methodTypeSig); locals = new ConstValue[numLocals]; - stack = new ConstValue[0]; - this.notifyQueue = notifyQueue; + stack = new ConstValue[maxStack]; + stackDepth = 0; int slot = 0; if (!isStatic) - locals[slot++] = new ConstValue(1); + locals[slot++] = unknownValue[0]; for (int i=0; i< paramTypes.length; i++) { int stackSize = TypeSignature.getTypeSize(paramTypes[i]); locals[slot] = unknownValue[stackSize-1]; @@ -307,67 +375,58 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } - public final void enqueue() { - if (nextOnQueue == null) { - this.nextOnQueue = notifyQueue.first; - notifyQueue.first = this; + public StackLocalInfo(StackLocalInfo original) { + locals = new ConstValue[original.locals.length]; + for (int i=0; i< locals.length; i++) { + if (original.locals[i] != null) + locals[i] = original.locals[i].copy(); + } + stack = new ConstValue[original.stack.length]; + for (int i=0; i< stackDepth; i++) { + if (original.stack[i] != null) + stack[i] = original.stack[i].copy(); } } - - public void constantChanged() { - enqueue(); - } - + public StackLocalInfo poppush(int pops, ConstValue push) { - ConstValue[] newStack - = new ConstValue[stack.length - pops + push.stackSize]; - ConstValue[] newLocals = (ConstValue[]) locals.clone(); - System.arraycopy(stack, 0, newStack, 0, stack.length-pops); - newStack[stack.length-pops] = push.copy(); - return new StackLocalInfo(newStack, newLocals, notifyQueue); + for (int i = pops; i>= 0; i--) + stack[--stackDepth] = null; + stack[stackDepth] = push; + stackDepth += push.stackSize; + return this; } public StackLocalInfo pop(int pops) { - ConstValue[] newStack - = new ConstValue[stack.length - pops]; - ConstValue[] newLocals = (ConstValue[]) locals.clone(); - System.arraycopy(stack, 0, newStack, 0, stack.length-pops); - return new StackLocalInfo(newStack, newLocals, notifyQueue); + for (int i = pops; i>= 0; i--) + stack[--stackDepth] = null; + return this; } public StackLocalInfo dup(int count, int depth) { - ConstValue[] newStack - = new ConstValue[stack.length + count]; - ConstValue[] newLocals = (ConstValue[]) locals.clone(); - if (depth == 0) - System.arraycopy(stack, 0, newStack, 0, stack.length); - else { - int pos = stack.length - count - depth; - System.arraycopy(stack, 0, newStack, 0, pos); - for (int i=0; i < count; i++) - newStack[pos++] = copy(stack[stack.length-count + i]); - for (int i=0; i < depth; i++) - newStack[pos++] = copy(stack[stack.length-count-depth + i]); + int bottom = stackDepth - count - depth; + System.arraycopy(stack, stackDepth - count, + stack, stackDepth, count); + if (depth > 0) { + System.arraycopy(stack, bottom, + stack, bottom + count, depth); + System.arraycopy(stack, stackDepth, + stack, bottom, count); } - for (int i=0; i < count; i++) - newStack[stack.length+i] = copy(stack[stack.length-count + i]); - return new StackLocalInfo(newStack, newLocals, notifyQueue); + stackDepth += count; + return this; } public StackLocalInfo swap() { - ConstValue[] newStack - = new ConstValue[stack.length]; - ConstValue[] newLocals = (ConstValue[]) locals.clone(); - System.arraycopy(stack, 0, newStack, 0, stack.length - 2); - newStack[stack.length-2] = stack[stack.length-1].copy(); - newStack[stack.length-1] = stack[stack.length-2].copy(); - return new StackLocalInfo(newStack, newLocals, notifyQueue); + ConstValue tmp = stack[stackDepth - 1]; + stack[stackDepth-1] = stack[stackDepth-2]; + stack[stackDepth-2] = tmp; + return this; } public StackLocalInfo copy() { ConstValue[] newStack = (ConstValue[]) stack.clone(); ConstValue[] newLocals = (ConstValue[]) locals.clone(); - return new StackLocalInfo(newStack, newLocals, notifyQueue); + return new StackLocalInfo(newStack, newLocals, stackDepth); } public ConstValue getLocal(int slot) { @@ -382,82 +441,23 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { locals[slot] = value; if (value != null && value.stackSize == 2) locals[slot+1] = null; - for (int i=0; i< locals.length; i++) { - if (locals[i] != null - && locals[i].value instanceof JSRTargetInfo) { - JSRTargetInfo jsrInfo = (JSRTargetInfo)locals[i].value; - if (!jsrInfo.uses(slot)) { - jsrInfo = jsrInfo.copy(); - locals[i] = locals[i].copy(); - locals[i].value = jsrInfo; - jsrInfo.addUsed(slot); - } - } - } - for (int i=0; i< stack.length; i++) { - if (stack[i] != null - && stack[i].value instanceof JSRTargetInfo) { - JSRTargetInfo jsrInfo = (JSRTargetInfo)stack[i].value; - if (!jsrInfo.uses(slot)) { - jsrInfo = jsrInfo.copy(); - stack[i] = stack[i].copy(); - stack[i].value = jsrInfo; - jsrInfo.addUsed(slot); - } - } - } return this; } - public StackLocalInfo mergeRetLocals(JSRTargetInfo jsrTargetInfo, - StackLocalInfo retInfo) { - for (int slot = 0; slot < locals.length; slot++) { - if (jsrTargetInfo.uses(slot)) - locals[slot] = retInfo.locals[slot]; - } - locals[retInfo.instr.getLocalSlot()] = null; - - for (int i=0; i< locals.length; i++) { - if (locals[i] != null - && locals[i].value instanceof JSRTargetInfo) { - JSRTargetInfo jsrInfo = (JSRTargetInfo) locals[i].value; - jsrInfo = jsrInfo.copy(); - locals[i] = locals[i].copy(); - locals[i].value = jsrInfo; - for (int slot = 0; slot < locals.length; slot++) { - if (jsrTargetInfo.uses(slot)) - jsrInfo.addUsed(slot); - } - } - } - for (int i=0; i< stack.length; i++) { - if (stack[i] != null - && stack[i].value instanceof JSRTargetInfo) { - JSRTargetInfo jsrInfo = (JSRTargetInfo)stack[i].value; - jsrInfo = jsrInfo.copy(); - stack[i] = stack[i].copy(); - stack[i].value = jsrInfo; - for (int slot = 0; slot < locals.length; slot++) { - if (jsrTargetInfo.uses(slot)) - jsrInfo.addUsed(slot); - } - } + public void mergeOneLocal(int slot, ConstValue cv) { + if (locals[slot] != null) { + if (cv == null) + // Other can be not initialized + // implies local can be not initialized + locals[slot] = null; + else + locals[slot].merge(cv); } - return this; } - + public void merge(StackLocalInfo other) { - for (int i=0; i < locals.length; i++) { - if (locals[i] != null) { - if (other.locals[i] == null) { - locals[i].constantChanged(); - locals[i] = null; - enqueue(); - } else { - locals[i].merge(other.locals[i]); - } - } - } + for (int i=0; i < locals.length; i++) + mergeOneLocal(i, other.locals[i]); if (stack.length != other.stack.length) throw new jode.AssertError("stack length differs"); for (int i=0; i < stack.length; i++) { @@ -470,7 +470,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { public String toString() { return "Locals: "+Arrays.asList(locals) - +"Stack: "+Arrays.asList(stack)+ "Instr: "+instr; + +"Stack: "+Arrays.asList(stack); } } @@ -506,19 +506,382 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { private static ConstantInfo unknownConstInfo = new ConstantInfo(); - public ConstantAnalyzer() { + /** + * The block info contains the info needed for a single block. + */ + private class BlockInfo implements ConstantListener { + + int nr; + Block block; + BlockInfo nextTodo; + + int constantFlow = -1; + /** + * The state of the locals and stack before this block is + * executed. + */ + StackLocalInfo before; + + /** + * The state of the locals and stack after this block is + * executed, but before the last jump instruction is done. + * So for conditional jumps the stack still contains the + * operands. + */ + StackLocalInfo after; + + /** + * The JsrInfo of jsrs that must be visited to reach + * this block. + */ + JsrInfo[] jsrInfos; + + public BlockInfo(int nr, Block block) { + this.nr = nr; + this.block = block; + } + + public boolean isReachable() { + return before != null; + } + + public void mergeOneLocal(int slot, ConstValue cv) { + before.mergeOneLocal(slot, cv); + } + + public void mergeBefore(StackLocalInfo info, + JsrInfo[] newJsrInfos) { + if (before == null) { + System.err.println("mergeBefore:::"+info); + before = new StackLocalInfo(info); + this.jsrInfos = new JsrInfo[newJsrInfos.length]; + for (int i = 0; i < newJsrInfos.length; i++) + jsrInfos[i] = newJsrInfos[i].copy(); + modifiedQueue.enqueue(this); + } else { + System.err.println("merging:::: "+before+":::AND:::"+info); + before.merge(info); + int newJsrCount = 0; + int ptr = 0; + for (int i = 0; i < jsrInfos.length; i++) { + for (int j = ptr; j < newJsrInfos.length; j++) { + if (newJsrInfos[j].jsrTarget + == jsrInfos[i].jsrTarget) { + jsrInfos[newJsrCount] = jsrInfos[i]; + jsrInfos[newJsrCount].merge(newJsrInfos[j]); + newJsrCount++; + ptr = j+1; + break; + } + } + } + if (newJsrCount < jsrInfos.length) { + JsrInfo[] newJsr = new JsrInfo[newJsrCount]; + System.arraycopy(jsrInfos, 0, newJsr, 0, newJsrCount); + jsrInfos = newJsr; + } + if (constantFlow >= 0) + propagateAfter(); + } + } + + public void constantChanged() { + if (constantFlow >= 0) + propagateAfter(); + } + + public void useLocal(int slot) { + for (int i=0; i< jsrInfos.length; i++) + jsrInfos[i].addUsed(slot); + } + + public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo caller) { + if (constantFlow == 0) { + Instruction[] instrs = block.getInstructions(); + // remove the constantFlow info + constantInfos.remove(instrs[instrs.length-1]); + } + Block nextBlock = caller.block.getSuccs()[1]; + if (nextBlock == null) { + /* The calling jsr is just before a return. We don't + * have to fuzz around, since nobody is interested in + * constant values. + */ + return; + } + + ConstValue[] newLocals = (ConstValue[]) after.locals.clone(); + for (int slot = 0; slot < newLocals.length; slot++) { + if (!myJsrInfo.uses(slot)) + newLocals[slot] = caller.after.locals[slot]; + else + caller.useLocal(slot); + } + StackLocalInfo nextInfo + = new StackLocalInfo(after.stack, newLocals, after.stackDepth); + int newCount = jsrInfos.length - 1; + while (jsrInfos[newCount].jsrTarget != myJsrInfo.jsrTarget) + newCount--; + JsrInfo[] retJsrInfos = new JsrInfo[newCount]; + System.arraycopy(jsrInfos, 0, + retJsrInfos, 0, newCount); + infos[nextBlock.getBlockNr()].mergeBefore(nextInfo, retJsrInfos); + } + + public void propagateAfter() { + Instruction[] instrs = block.getInstructions(); + Instruction instr = instrs[instrs.length-1]; + int opcode = instr.getOpcode(); + switch (opcode) { + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: { + int size = 1; + ConstValue stacktop = after.getStack(1); + ConstValue other = null; + boolean known = stacktop.value != ConstValue.VOLATILE; + if (opcode >= opc_if_icmpeq && opcode <= opc_if_acmpne) { + other = after.getStack(2); + size = 2; + known &= other.value != ConstValue.VOLATILE; + } + StackLocalInfo nextInfo = after.copy().pop(size); + if (known) { + if (constantFlow >= 0) + /* Nothing changed... */ + return; + int opc_mask; + if (opcode >= opc_if_acmpeq) { + if (opcode >= opc_ifnull) { + opc_mask = stacktop.value == null + ? CMP_EQUAL_MASK : CMP_GREATER_MASK; + opcode -= opc_ifnull; + } else { + opc_mask = stacktop.value == other.value + ? CMP_EQUAL_MASK : CMP_GREATER_MASK; + opcode -= opc_if_acmpeq; + } + } else { + int value = ((Integer) stacktop.value).intValue(); + if (opcode >= opc_if_icmpeq) { + int val1 = ((Integer) other.value).intValue(); + opc_mask = (val1 == value ? CMP_EQUAL_MASK + : val1 < value ? CMP_LESS_MASK + : CMP_GREATER_MASK); + opcode -= opc_if_icmpeq; + } else { + opc_mask = (value == 0 ? CMP_EQUAL_MASK + : value < 0 ? CMP_LESS_MASK + : CMP_GREATER_MASK); + opcode -= opc_ifeq; + } + } + + constantFlow = ((opc_mask & (1<= 0) + /* Nothing changed... */ + return; + Instruction pc; + int value = ((Integer) stacktop.value).intValue(); + int[] values = instr.getValues(); + constantFlow = Arrays.binarySearch(values, value); + if (constantFlow < 0) + constantFlow = values.length; + ConstantInfo constInfo = new ConstantInfo + (CONSTANTFLOW, new Integer(constantFlow)); + constantInfos.put(instr, constInfo); + stacktop.addConstantListener(this); + Block constantSucc = block.getSuccs()[constantFlow]; + if (constantSucc != null) + infos[constantSucc.getBlockNr()] + .mergeBefore(nextInfo, jsrInfos); + } else { + constantInfos.remove(instr); + Block[] succs = block.getSuccs(); + for (int i=0; i < succs.length; i++) { + if (i != constantFlow) { + if (succs[i] != null) + infos[succs[i].getBlockNr()] + .mergeBefore(nextInfo, jsrInfos); + } + } + constantFlow = -1; + } + break; + } + case opc_jsr: { + // XXXXXXXXXXXX System.err + dumpBlockInfo(GlobalOptions.err); + GlobalOptions.err.println(instr); + + BlockInfo target = infos[block.getSuccs()[0].getBlockNr()]; + ConstValue result = new ConstValue(target); + int jsrInfosCount = jsrInfos.length; + for (int i=0; i< jsrInfosCount; i++) { + if (jsrInfos[i].jsrTarget == target) { + // recursive jsrs are forbidden. But maybe the jsr + // just terminated without a ret. + jsrInfosCount = i; + } + } + JsrInfo[] newJsrInfos = new JsrInfo[jsrInfosCount + 1]; + System.arraycopy(jsrInfos, 0, newJsrInfos, 0, jsrInfosCount); + newJsrInfos[jsrInfosCount] = new JsrInfo(target); + newJsrInfos[jsrInfosCount].addCaller(this); + constantFlow = 0; + ConstantInfo constInfo = new ConstantInfo + (CONSTANTFLOW, new Integer(0)); + constantInfos.put(instr, constInfo); + target.mergeBefore(after.copy().poppush(0, result), + newJsrInfos); + break; + } + case opc_ret: { + ConstValue result = after.getLocal(instr.getLocalSlot()); + BlockInfo jsrTarget = (BlockInfo) result.value; + + int i = jsrInfos.length-1; + while (jsrInfos[i].jsrTarget != jsrTarget) + i--; + jsrInfos[i].setRetInfo(this); + break; + } + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: + case opc_areturn: case opc_return: + case opc_athrow: + break; + default: { + Block succ = block.getSuccs()[0]; + if (succ != null) + infos[succ.getBlockNr()].mergeBefore(after, jsrInfos); + } + } + } + + public void analyze() { + System.err.println("analyze:"); + StackLocalInfo info = before.copy(); + Handler[] handlers = block.getCatchers(); + + if (handlers.length > 0) { + ConstValue[] newStack = new ConstValue[info.stack.length]; + newStack[0] = unknownValue[0]; + StackLocalInfo catchInfo = + new StackLocalInfo(newStack, info.locals, 1); + + for (int i=0; i< handlers.length; i++) { + if (handlers[i].getType() != null) + Main.getClassBundle().reachableClass + (handlers[i].getType()); + + infos[handlers[i].getCatcher().getBlockNr()] + .mergeBefore(catchInfo, jsrInfos); + } + } + + Instruction[] instrs = block.getInstructions(); + for (int idx = 0 ; idx < instrs.length; idx++) { + Instruction instr = instrs[idx]; + info = handleOpcode(instr, info, this); + if (instr.isStore() && handlers.length > 0) { + int slot = instr.getLocalSlot(); + ConstValue newValue = info.locals[slot]; + for (int i=0; i< handlers.length; i++) { + infos[handlers[i].getCatcher().getBlockNr()] + .mergeOneLocal(slot, info.locals[slot]); + if (newValue.stackSize > 1) + infos[handlers[i].getCatcher().getBlockNr()] + .mergeOneLocal(slot+1, info.locals[slot+1]); + } + } + } + after = info; + dumpInfo(GlobalOptions.err); + propagateAfter(); + } + + public void dumpInfo(PrintWriter output) { + output.println("/-["+nr+"]-"+before); + block.dumpCode(output); + output.println("\\-["+nr+"]-"+after); + } + + public String toString() { + return "BlockAnalyzer["+nr+"]"; + } } - public void mergeInfo(Instruction instr, - StackLocalInfo info) { - if (instr.getTmpInfo() == null) { - instr.setTmpInfo(info); - info.instr = instr; - info.enqueue(); - } else - ((StackLocalInfo)instr.getTmpInfo()).merge(info); + + /** + * The TodoQueue is a linked list of BlockInfo + * + * There is only one TodoQueue, the modifiedQueue in analyzeCode + * + * The queue operations are in StackLocalInfo. + */ + static class TodoQueue { + BlockInfo first; + + public void enqueue(BlockInfo info) { + if (info.nextTodo == null) { + info.nextTodo = first; + first = info; + } + } + + public BlockInfo dequeue() { + BlockInfo result = first; + if (result != null) { + first = result.nextTodo; + result.nextTodo = null; + } + return result; + } } + public void fieldNotConstant(FieldIdentifier fi) { + ConstValue value = (ConstValue) fieldDependencies.remove(fi); + if (value != null) + value.constantChanged(); + fi.removeFieldListener(methodIdent); + fi.setNotConstant(); + } + private ClassInfo canonizeIfaceRef(ClassInfo clazz, Reference ref) { while (clazz != null) { if (clazz.findMethod(ref.getName(), ref.getType()) != null) @@ -534,7 +897,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { return null; } - public Identifier canonizeReference(Instruction instr) { + private Identifier canonizeReference(Instruction instr) { Reference ref = instr.getReference(); Identifier ident = Main.getClassBundle().getIdentifier(ref); String clName = ref.getClazz(); @@ -587,11 +950,11 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { return ident; } - public void handleReference(Reference ref, boolean isVirtual) { + void handleReference(Reference ref, boolean isVirtual) { Main.getClassBundle().reachableReference(ref, isVirtual); } - public void handleClass(String clName) { + void handleClass(String clName) { int i = 0; while (i < clName.length() && clName.charAt(i) == '[') i++; @@ -601,126 +964,67 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } - public void handleOpcode(StackLocalInfo info, Identifier fieldListener) { - Instruction instr = info.instr; - info.constInfo = unknownConstInfo; - + StackLocalInfo handleOpcode(Instruction instr, + StackLocalInfo info, BlockInfo block) { + constantInfos.remove(instr); int opcode = instr.getOpcode(); - Handler[] handlers = bytecode.getExceptionHandlers(); - for (int i=0; i< handlers.length; i++) { - if (handlers[i].start.getAddr() <= instr.getAddr() - && handlers[i].end.getAddr() >= instr.getAddr()) - mergeInfo(handlers[i].catcher, - info.poppush(info.stack.length, unknownValue[0])); - } ConstValue result; switch (opcode) { case opc_nop: - mergeInfo(instr.getNextByAddr(), info.pop(0)); - break; + return info.pop(0); case opc_ldc: case opc_ldc2_w: result = new ConstValue(instr.getConstant()); - mergeInfo(instr.getNextByAddr(), info.poppush(0, result)); - break; + return info.poppush(0, result); case opc_iload: case opc_lload: case opc_fload: case opc_dload: case opc_aload: result = info.getLocal(instr.getLocalSlot()); - if (result == null) { - dumpStackLocalInfo(); - System.err.println(info); - System.err.println(instr); - } if (result.value != ConstValue.VOLATILE) { - info.constInfo = new ConstantInfo(CONSTANT, result.value); - result.addConstantListener(info.constInfo); + ConstantInfo constInfo + = new ConstantInfo(CONSTANT, result.value); + result.addConstantListener(constInfo); + constantInfos.put(instr, constInfo); } - mergeInfo(instr.getNextByAddr(), - info.poppush(0, result) - .setLocal(instr.getLocalSlot(), result.copy())); - break; + return info.poppush(0, result) + .setLocal(instr.getLocalSlot(), result); case opc_iaload: case opc_laload: case opc_faload: case opc_daload: case opc_aaload: case opc_baload: case opc_caload: case opc_saload: { -// ConstValue array = info.getStack(2); -// ConstValue index = info.getStack(1); -// ConstValue newValue = null; -// if (index.value != index.ConstValue.VOLATILE -// && array.value != array.ConstValue.VOLATILE -// && array.value != null) { -// int indexVal = ((Integer) index.value).intValue(); -// Object content; -// switch(opcode) { -// case opc_baload: -// content = new Integer -// (array.value instanceof byte[] -// ? ((byte[])array.value)[indexVal] -// : ((boolean[])array.value)[indexVal] ? 1 : 0); -// case opc_caload: -// content = new Integer(((char[])array.value)[indexVal]); -// break; -// case opc_saload: -// content = new Integer(((short[])array.value)[indexVal]); -// break; -// case opc_iaload: -// case opc_laload: -// case opc_faload: -// case opc_daload: -// case opc_aaload: -// content = Array.get(array.value, indexVal); -// break; -// default: -// throw new jode.AssertError("Can't happen."); -// } -// result = new ConstValue(content); -// array.addConstantListener(result); -// index.addConstantListener(result); -// } else { result = unknownValue[(opcode == opc_laload || opcode == opc_daload) ? 1 : 0]; -// } - mergeInfo(instr.getNextByAddr(), info.poppush(2, result)); - break; + return info.poppush(2, result); } case opc_istore: case opc_fstore: case opc_astore: { - result = info.getStack(1); - if (result.value instanceof JSRTargetInfo) - info.constInfo.flags |= RETASTORE; - mergeInfo(instr.getNextByAddr(), - info.pop(1).setLocal(instr.getLocalSlot(), result)); - break; + int slot = instr.getLocalSlot(); + block.useLocal(slot); + return info.pop(1).setLocal(slot, info.getStack(1)); } case opc_lstore: case opc_dstore: { - mergeInfo(instr.getNextByAddr(), - info.pop(2).setLocal(instr.getLocalSlot(), info.getStack(2))); - break; + int slot = instr.getLocalSlot(); + block.useLocal(slot); + block.useLocal(slot + 1); + return info.pop(2).setLocal(slot, info.getStack(2)); } case opc_iastore: case opc_lastore: case opc_fastore: case opc_dastore: case opc_aastore: case opc_bastore: case opc_castore: case opc_sastore: { int size = (opcode == opc_lastore || opcode == opc_dastore) ? 2 : 1; - mergeInfo(instr.getNextByAddr(), info.pop(2+size)); - break; + return info.pop(2+size); } case opc_pop: - mergeInfo(instr.getNextByAddr(), info.pop(1)); - break; + return info.pop(1); case opc_pop2: - mergeInfo(instr.getNextByAddr(), info.pop(2)); - break; + return info.pop(2); case opc_dup: case opc_dup_x1: case opc_dup_x2: case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: - mergeInfo(instr.getNextByAddr(), - info.dup((opcode - (opc_dup - 3)) / 3, - (opcode - (opc_dup - 3)) % 3)); - break; + return info.dup((opcode - (opc_dup - 3)) / 3, + (opcode - (opc_dup - 3)) % 3); case opc_swap: - mergeInfo(instr.getNextByAddr(), info.swap()); - break; + return info.swap(); case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: @@ -881,15 +1185,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { default: throw new jode.AssertError("Can't happen."); } - info.constInfo = new ConstantInfo(CONSTANT, newValue); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); result = new ConstValue(newValue); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); value1.addConstantListener(result); value2.addConstantListener(result); } else result = unknownValue[size-1]; - mergeInfo(instr.getNextByAddr(), info.poppush(2*size, result)); - break; + return info.poppush(2*size, result); } case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: { int size = 1 + (opcode - opc_ineg & 1); @@ -916,14 +1220,14 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { default: throw new jode.AssertError("Can't happen."); } - info.constInfo = new ConstantInfo(CONSTANT, newValue); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); result = new ConstValue(newValue); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); value.addConstantListener(result); } else result = unknownValue[size-1]; - mergeInfo(instr.getNextByAddr(), info.poppush(size, result)); - break; + return info.poppush(size, result); } case opc_ishl: case opc_lshl: case opc_ishr: case opc_lshr: @@ -969,15 +1273,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { default: throw new jode.AssertError("Can't happen."); } - info.constInfo = new ConstantInfo(CONSTANT, newValue); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newValue); + constantInfos.put(instr, constInfo); result = new ConstValue(newValue); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); value1.addConstantListener(result); value2.addConstantListener(result); } else result = unknownValue[size-1]; - mergeInfo(instr.getNextByAddr(), info.poppush(size+1, result)); - break; + return info.poppush(size+1, result); } case opc_iinc: { ConstValue local = info.getLocal(instr.getLocalSlot()); @@ -988,9 +1292,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { local.addConstantListener(result); } else result = unknownValue[0]; - mergeInfo(instr.getNextByAddr(), - info.copy().setLocal(instr.getLocalSlot(), result)); - break; + return info.setLocal(instr.getLocalSlot(), result); } case opc_i2l: case opc_i2f: case opc_i2d: case opc_l2i: case opc_l2f: case opc_l2d: @@ -1016,9 +1318,10 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { default: throw new jode.AssertError("Can't happen."); } - info.constInfo = new ConstantInfo(CONSTANT, newVal); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); result = new ConstValue(newVal); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); stack.addConstantListener(result); } else { switch (opcode) { @@ -1030,8 +1333,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { result = unknownValue[0]; } } - mergeInfo(instr.getNextByAddr(), info.poppush(insize, result)); - break; + return info.poppush(insize, result); } case opc_i2b: case opc_i2c: case opc_i2s: { ConstValue stack = info.getStack(1); @@ -1049,15 +1351,14 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { break; } Integer newVal = new Integer(val); - info.constInfo = new ConstantInfo(CONSTANT, newVal); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); result = new ConstValue(newVal); - stack.addConstantListener(info.constInfo); + stack.addConstantListener(constInfo); stack.addConstantListener(result); } else result = unknownValue[0]; - mergeInfo(instr.getNextByAddr(), - info.poppush(1, result)); - break; + return info.poppush(1, result); } case opc_lcmp: { ConstValue val1 = info.getStack(4); @@ -1068,15 +1369,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { long value2 = ((Long) val1.value).longValue(); Integer newVal = new Integer(value1 == value2 ? 0 : value1 < value2 ? -1 : 1); - info.constInfo = new ConstantInfo(CONSTANT, newVal); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); result = new ConstValue(newVal); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); val1.addConstantListener(result); val2.addConstantListener(result); } else result = unknownValue[0]; - mergeInfo(instr.getNextByAddr(), info.poppush(4, result)); - break; + return info.poppush(4, result); } case opc_fcmpl: case opc_fcmpg: { ConstValue val1 = info.getStack(2); @@ -1090,15 +1391,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { : ( opcode == opc_fcmpg ? (value1 < value2 ? -1 : 1) : (value1 > value2 ? 1 : -1))); - info.constInfo = new ConstantInfo(CONSTANT, newVal); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); result = new ConstValue(newVal); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); val1.addConstantListener(result); val2.addConstantListener(result); } else result = unknownValue[0]; - mergeInfo(instr.getNextByAddr(), info.poppush(2, result)); - break; + return info.poppush(2, result); } case opc_dcmpl: case opc_dcmpg: { ConstValue val1 = info.getStack(4); @@ -1112,15 +1413,15 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { : ( opcode == opc_dcmpg ? (value1 < value2 ? -1 : 1) : (value1 > value2 ? 1 : -1))); - info.constInfo = new ConstantInfo(CONSTANT, newVal); + ConstantInfo constInfo = new ConstantInfo(CONSTANT, newVal); + constantInfos.put(instr, constInfo); result = new ConstValue(newVal); - result.addConstantListener(info.constInfo); + result.addConstantListener(constInfo); val1.addConstantListener(result); val2.addConstantListener(result); } else result = unknownValue[0]; - mergeInfo(instr.getNextByAddr(), info.poppush(4, result)); - break; + return info.poppush(4, result); } case opc_ifeq: case opc_ifne: case opc_iflt: case opc_ifge: @@ -1129,139 +1430,21 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { case opc_if_icmplt: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: case opc_if_acmpeq: case opc_if_acmpne: - case opc_ifnull: case opc_ifnonnull: { - int size = 1; - ConstValue stacktop = info.getStack(1); - ConstValue other = null; - boolean known = stacktop.value != ConstValue.VOLATILE; - if (opcode >= opc_if_icmpeq && opcode <= opc_if_acmpne) { - other = info.getStack(2); - size = 2; - known &= other.value != ConstValue.VOLATILE; - } - if (known) { - stacktop.addConstantListener(info); - if (other != null) - other.addConstantListener(info); - - Instruction pc = instr.getNextByAddr(); - int opc_mask; - if (opcode >= opc_if_acmpeq) { - if (opcode >= opc_ifnull) { - opc_mask = stacktop.value == null - ? CMP_EQUAL_MASK : CMP_GREATER_MASK; - opcode -= opc_ifnull; - } else { - opc_mask = stacktop.value == other.value - ? CMP_EQUAL_MASK : CMP_GREATER_MASK; - opcode -= opc_if_acmpeq; - } - } else { - int value = ((Integer) stacktop.value).intValue(); - if (opcode >= opc_if_icmpeq) { - int val1 = ((Integer) other.value).intValue(); - opc_mask = (val1 == value ? CMP_EQUAL_MASK - : val1 < value ? CMP_LESS_MASK - : CMP_GREATER_MASK); - opcode -= opc_if_icmpeq; - } else { - opc_mask = (value == 0 ? CMP_EQUAL_MASK - : value < 0 ? CMP_LESS_MASK - : CMP_GREATER_MASK); - opcode -= opc_ifeq; - } - } - - if ((opc_mask & (1< 0) { - iter.set(new Instruction(TypeSignature.getTypeSize(pt[0]) - + opc_pop - 1)); + newCode.add(Instruction.forOpcode(TypeSignature.getTypeSize(pt[0]) + + opc_pop - 1)); arg++; } else { - if (replacement == null) - iter.remove(); - else - iter.set(replacement); + if (replacement != null) + newCode.add(replacement); return; } for (int i=arg; i < pt.length; i++) - iter.add(new Instruction(TypeSignature.getTypeSize(pt[i]) - + opc_pop - 1)); + newCode.add(Instruction.forOpcode + (TypeSignature.getTypeSize(pt[i]) + + opc_pop - 1)); } + default: + throw new AssertError("Unexpected opcode"); } if (replacement != null) - iter.add(replacement); + newCode.add(replacement); } - public void appendJump(ListIterator iter, Instruction dest) { - /* Add a goto instruction after this opcode. */ - Instruction gotoInstr = new Instruction(Instruction.opc_goto); - gotoInstr.setSuccs(dest); - iter.add(gotoInstr); - } - - public void transformCode(BytecodeInfo bytecode) { - for (ListIterator iter = bytecode.getInstructions().listIterator(); - iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - ConstantInfo info = (ConstantInfo) instr.getTmpInfo(); - instr.setTmpInfo(null); - - if (info == null - || (info.flags & (RETURNINGJSR | RETASTORE)) == RETASTORE) { - /* This instruction can't be reached logically, or - * it is a return value astore, that should be removed */ - iter.remove(); - } else if ((info.flags & CONSTANT) != 0) { - if (instr.getOpcode() > opc_ldc2_w) { - Instruction ldcInstr - = new Instruction(info.constant instanceof Long - || info.constant instanceof Double - ? opc_ldc2_w : opc_ldc); - ldcInstr.setConstant(info.constant); - replaceWith(iter, instr, ldcInstr); + public void transformCode(BasicBlocks bb) { + BitSet reachable = (BitSet) bbInfos.remove(bb); + Block[] blocks = bb.getBlocks(); + Handler[] handlers = bb.getExceptionHandlers(); + + Block newStartBlock = bb.getStartBlock(); + int newBlockCtr = 0; + int newHandlerCtr = 0; + next_handler: + for (int i = 0; i < handlers.length; i++) { + int start = handlers[i].getStart().getBlockNr(); + int end = handlers[i].getEnd().getBlockNr(); + while (!reachable.get(end)) { + if (start == end) + /* handler not reachable, check next one. */ + continue next_handler; + start++; + } + while (!reachable.get(end)) { + end--; + } + handlers[i].setStart(blocks[start]); + handlers[i].setEnd(blocks[start]); + /* Catcher is always reachable */ + handlers[newHandlerCtr++] = handlers[i]; + } + for (int i=0; i < blocks.length; i++) { + if (!reachable.get(i)) + continue; + blocks[newBlockCtr] = blocks[i]; + Instruction[] oldCode = blocks[i].getInstructions(); + Block[] succs = blocks[i].getSuccs(); + ArrayList newCode = new ArrayList(oldCode.length); + for (int idx = 0; idx < oldCode.length; idx++) { + Instruction instr = oldCode[idx]; + ConstantInfo info = (ConstantInfo) constantInfos.remove(instr); + if ((info.flags & CONSTANT) != 0) { + Instruction ldcInstr = new ConstantInstruction + (info.constant instanceof Long + || info.constant instanceof Double + ? opc_ldc2_w : opc_ldc, info.constant); if (GlobalOptions.verboseLevel > 2) GlobalOptions.err.println - (bytecode + ": Replacing " + instr - + " with constant " + info.constant); - } - } else if ((info.flags & CONSTANTFLOW) != 0) { - Instruction pc = (Instruction) info.constant; - if (instr.getOpcode() >= opc_if_icmpeq - && instr.getOpcode() <= opc_if_acmpne) - iter.set(new Instruction(opc_pop2)); - else - iter.set(new Instruction(opc_pop)); - if (GlobalOptions.verboseLevel > 2) - GlobalOptions.err.println - (bytecode + ": Replacing " + instr - + " with goto " + pc.getAddr()); - while (iter.hasNext()) { - ConstantInfo nextinfo = (ConstantInfo) - ((Instruction) iter.next()).getTmpInfo(); - if (nextinfo != null) { - Instruction nextInstr = (Instruction) iter.previous(); - if (pc != nextInstr) - appendJump(iter, pc); - break; - } - /* Next instruction can't be reached logically */ - iter.remove(); - } - - } else { - int opcode = instr.getOpcode(); - switch (opcode) { - case opc_nop: - iter.remove(); - break; - - case opc_jsr: - ConstantInfo jsrinfo = (ConstantInfo) - instr.getSingleSucc().getTmpInfo(); - if ((jsrinfo.flags & RETURNINGJSR) != 0) - /* A normal jsr, don't change it */ + (bb + ": Replacing " + instr + + " with constant " + info.constant); + replaceWith(newCode, instr, ldcInstr); + } else if ((info.flags & CONSTANTFLOW) != 0) { + int succnr = ((Integer)info.constant).intValue(); + replaceWith(newCode, instr, null); + if (GlobalOptions.verboseLevel > 2) + GlobalOptions.err.println + (bb + ": Removing " + instr); + succs = new Block[] { succs[succnr] }; + } else { + int opcode = instr.getOpcode(); + switch (opcode) { + case opc_nop: break; - /* This means, the jsr will never return. We - * replace it with a goto, the jsr will transform - * itself to remove the astore operation. - */ - Instruction gotoInstr = new Instruction(opc_goto); - gotoInstr.setSuccs(instr.getSingleSucc()); - iter.set(gotoInstr); - /* fall through */ - case opc_goto: - case opc_ifeq: case opc_ifne: - case opc_iflt: case opc_ifge: - case opc_ifgt: case opc_ifle: - case opc_ifnull: case opc_ifnonnull: - case opc_if_icmpeq: case opc_if_icmpne: - case opc_if_icmplt: case opc_if_icmpge: - case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - - while (iter.hasNext()) { - ConstantInfo nextinfo = (ConstantInfo) - ((Instruction) iter.next()).getTmpInfo(); - if (nextinfo != null - && ((nextinfo.flags & (RETURNINGJSR | RETASTORE)) - != RETASTORE)) { - - Instruction nextInstr - = (Instruction) iter.previous(); - if (instr.getSingleSucc() == nextInstr) { - /* put iter in sane state */ - iter.previous(); - iter.next(); - replaceWith(iter, instr, null); - } - break; + case opc_putstatic: + case opc_putfield: { + Reference ref = instr.getReference(); + FieldIdentifier fi = (FieldIdentifier) + Main.getClassBundle().getIdentifier(ref); + if (fi != null + && (Main.stripping & Main.STRIP_UNREACH) != 0 + && !fi.isReachable()) { + replaceWith(newCode, instr, null); } - /* Next instruction can be removed */ - iter.remove(); + break; } - break; - - case opc_putstatic: - case opc_putfield: { - Reference ref = instr.getReference(); - FieldIdentifier fi = (FieldIdentifier) - Main.getClassBundle().getIdentifier(ref); - if (fi != null - && (Main.stripping & Main.STRIP_UNREACH) != 0 - && !fi.isReachable()) { - replaceWith(iter, instr, null); + default: + newCode.add(instr); } - break; - } } } + blocks[i].setCode((Instruction[]) newCode.toArray(oldCode), succs); + newBlockCtr++; + } + if (newBlockCtr < blocks.length) { + Block[] newBlocks = new Block[newBlockCtr]; + Handler[] newHandlers = new Handler[newHandlerCtr]; + System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr); + System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr); + bb.setBlocks(newBlocks, newStartBlock); + bb.setExceptionHandlers(newHandlers); } } } diff --git a/jode/jode/obfuscator/modules/LocalOptimizer.java b/jode/jode/obfuscator/modules/LocalOptimizer.java index c8928f6..cca154c 100644 --- a/jode/jode/obfuscator/modules/LocalOptimizer.java +++ b/jode/jode/obfuscator/modules/LocalOptimizer.java @@ -25,6 +25,8 @@ import jode.AssertError; import jode.GlobalOptions; ///#def COLLECTIONS java.util +import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; ///#enddef @@ -81,18 +83,14 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { String name; String type; - Vector usingInstrs = new Vector(); Vector conflictingLocals = new Vector(); + String id; // for debugging purposes only. int size; int newSlot = -1; LocalInfo() { } - LocalInfo(InstrInfo instr) { - usingInstrs.addElement(instr); - } - void conflictsWith(LocalInfo l) { if (shadow != null) { getReal().conflictsWith(l); @@ -114,82 +112,114 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { if (this == l) return; shadow = l; - if (shadow.name == null) { - shadow.name = name; - shadow.type = type; - } - Enumeration enum = usingInstrs.elements(); - while (enum.hasMoreElements()) { - InstrInfo instr = (InstrInfo) enum.nextElement(); - instr.local = l; - l.usingInstrs.addElement(instr); + if (l.name == null) { + l.name = name; + l.type = type; } + if (id.compareTo(l.id) < 0) + l.id = id; } - public int getFirstAddr() { - int minAddr = Integer.MAX_VALUE; - Enumeration enum = usingInstrs.elements(); - while (enum.hasMoreElements()) { - InstrInfo info = (InstrInfo) enum.nextElement(); - if (info.instr.getAddr() < minAddr) - minAddr = info.instr.getAddr(); - } - return minAddr; + void generateID(int blockNr, int instrNr) { + char[] space = new char[5]; + space[0] = (char) ('0' + blockNr / 10); + space[1] = (char) ('0' + blockNr % 10); + space[2] = (char) ('a' + instrNr / (26*26)); + space[3] = (char) ('a' + (instrNr / 26) % 26); + space[4] = (char) ('a' + instrNr % 26); + id = new String(space); + } + + public String toString() { + return id; } } private static class TodoQueue { - public final InstrInfo LAST = new InstrInfo(); - InstrInfo first = LAST; + BlockInfo first = null; + BlockInfo last = null; - public void add(InstrInfo info) { - if (info.nextTodo == null) { + public void add(BlockInfo info) { + if (info.nextTodo == null && info != last) { /* only enqueue if not already on queue */ info.nextTodo = first; first = info; + if (first == null) + last = info; } } public boolean isEmpty() { - return first == LAST; + return first == null; } - public InstrInfo remove() { - if (first == LAST) - throw new NoSuchElementException(); - InstrInfo result = first; + public BlockInfo remove() { + BlockInfo result = first; first = result.nextTodo; result.nextTodo = null; + if (first == null) + last = null; return result; } } - + + BasicBlocks bb; + + TodoQueue changedInfos; + InstrInfo firstInfo; + LocalInfo[] paramLocals; + BlockInfo[] blockInfos; + int maxlocals; /** * This class contains information for each instruction. */ - static class InstrInfo { + class BlockInfo { /** - * The next changed InstrInfo, or null, if this instr info did + * The next changed BlockInfo, or null, if this instr info did * not changed. */ - InstrInfo nextTodo; + BlockInfo nextTodo; /** - * The LocalInfo that this instruction manipulates, or null - * if this is not an ret, iinc, load or store instruction. + * The local infos for each Instruction. The index is the + * instruction number. */ - LocalInfo local; + LocalInfo[] instrLocals; + + /** + * The LocalInfo, whose values are read from a previous block. + * Index is the slot number. + */ + LocalInfo[] ins; + + /** + * The LocalInfo, written in this block. + * Index is the slot number. + */ + LocalInfo[] gens; + + /** + * The predecessors for this block. + */ + Collection preds = new ArrayList(); + + /** + * The tryBlocks, for which this block is the catcher. + */ + Collection tryBlocks = new ArrayList(); + /** * For each slot, this contains the InstrInfo of one of the * next Instruction, that may read from that slot, without - * prior writing. */ + * prior writing. + */ InstrInfo[] nextReads; /** - * This only has a value for ret instructions. In that case - * this bitset contains all locals, that may be used between - * jsr and ret. + * This only has a value for blocks countaining a ret. In + * that case this bitset contains all locals, that may be used + * between jsr and ret. */ BitSet usedBySub; /** @@ -208,27 +238,171 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { * If this instruction is a ret, this contains the single * allowed jsr target to which this ret belongs. */ - InstrInfo jsrTargetInfo; - /** - * The Instruction of this info - */ - Instruction instr; + BlockInfo jsrTargetInfo; /** - * The next info in the chain. + * The underlying basic block. */ - InstrInfo nextInfo; - } + Block block; + + + public BlockInfo(int blockNr, Block block) { + this.block = block; + ins = new LocalInfo[bb.getMaxLocals()]; + gens = new LocalInfo[bb.getMaxLocals()]; + Instruction[] instrs = block.getInstructions(); + instrLocals = new LocalInfo[instrs.length]; + for (int instrNr = 0; instrNr < instrs.length; instrNr++) { + Instruction instr = instrs[instrNr]; + if (instr.hasLocal()) { + int slot = instr.getLocalSlot(); + LocalInfo local = new LocalInfo(); + instrLocals[instrNr] = local; + LocalVariableInfo lvi = instr.getLocalInfo(); + local.name = lvi.getName(); + local.type = lvi.getType(); + local.size = 1; + local.generateID(blockNr, instrNr); + switch (instr.getOpcode()) { + case opc_lload: case opc_dload: + local.size = 2; + /* fall through */ + case opc_iload: case opc_fload: case opc_aload: + case opc_iinc: + /* this is a load instruction */ + if (gens[slot] == null) { + ins[slot] = local; + gens[slot] = local; + changedInfos.add(this); + } else { + gens[slot].combineInto(local); + } + break; - BytecodeInfo bc; + case opc_ret: + /* this is a ret instruction */ + usedBySub = new BitSet(); + if (gens[slot] == null) { + ins[slot] = local; + gens[slot] = local; + changedInfos.add(this); + } else { + gens[slot].combineInto(local); + } + break; - TodoQueue changedInfos; - InstrInfo firstInfo; - Hashtable instrInfos; - boolean produceLVT; - int maxlocals; + case opc_lstore: case opc_dstore: + local.size = 2; + /* fall through */ + case opc_istore: case opc_fstore: case opc_astore: + gens[slot] = local; + break; - LocalInfo[] paramLocals; + default: + throw new AssertError + ("Illegal opcode for SlotInstruction"); + } + } + } + } + + void promoteIn(int slot, LocalInfo local) { + if (gens[slot] == null) { + changedInfos.add(this); + ins[slot] = local; + } else { + gens[slot].combineInto(local); + } + } + + void promoteInToAll(int slot, LocalInfo local) { + if (ins[slot] == null) { + changedInfos.add(this); + ins[slot] = local; + } else + ins[slot].combineInto(local); + + if (gens[slot] != null) { + gens[slot].combineInto(local); + for (int i=0; i< instrLocals.length; i++) { + if (instrLocals[i] != null + && block.getInstructions()[i].getLocalSlot() == slot) + instrLocals[i].combineInto(local); + } + } + } + + public void promoteIns() { + for (int i=0; i < ins.length; i++) { + if (ins[i] != null) { + for (Iterator iter = preds.iterator(); iter.hasNext();) { + BlockInfo pred = (BlockInfo) iter.next(); + pred.promoteIn(i, ins[i]); + } + + for (Iterator iter = tryBlocks.iterator(); + iter.hasNext();) { + BlockInfo pred = (BlockInfo) iter.next(); + pred.promoteInToAll(i, ins[i]); + } + } + } +// if (prevInstr.getOpcode() == opc_jsr) { +// /* Prev instr is a jsr, promote reads to the +// * corresponding ret. +// */ +// InstrInfo jsrInfo = +// (InstrInfo) instrInfos.get(prevInstr.getSingleSucc()); +// if (jsrInfo.retInfo != null) { +// /* Now promote reads that are modified by the +// * subroutine to the ret, and those that are not +// * to the jsr instruction. +// */ +// promoteReads(info, jsrInfo.retInfo.instr, +// jsrInfo.retInfo.usedBySub, false); +// promoteReads(info, prevInstr, +// jsrInfo.retInfo.usedBySub, true); +// } +// } + } + + public void generateConflicts() { + LocalInfo[] active = (LocalInfo[]) ins.clone(); + Instruction[] instrs = block.getInstructions(); + for (int instrNr = 0; instrNr < instrs.length; instrNr++) { + Instruction instr = instrs[instrNr]; + if (instr.isStore()) { + /* This is a store. It conflicts with every local, which + * is active at this point. + */ + for (int i=0; i < maxlocals; i++) { + if (i != info.instr.getLocalSlot() + && active[i] != null) + instrLocals[instrNr].conflictsWith(active[i]); + + + if (info.nextInfo.nextReads[i] != null + && info.nextInfo.nextReads[i].jsrTargetInfo != null) { + Instruction[] jsrs = info.nextInfo.nextReads[i] + .jsrTargetInfo.instr.getPreds(); + for (int j=0; j< jsrs.length; j++) { + InstrInfo jsrInfo + = (InstrInfo) instrInfos.get(jsrs[j]); + for (int k=0; k < maxlocals; k++) { + if (!info.nextInfo.nextReads[i].usedBySub + .get(k) + && jsrInfo.nextReads[k] != null) + info.local.conflictsWith + (jsrInfo.nextReads[k].local); + } + } + } + } + } + } + } + } + public LocalOptimizer() { } @@ -254,108 +428,41 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { return result; } - void promoteReads(InstrInfo info, Instruction preInstr, - BitSet mergeSet, boolean inverted) { - InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr); - int omitLocal = -1; - if (preInstr.getOpcode() >= opc_istore - && preInstr.getOpcode() <= opc_astore) { - /* This is a store */ - omitLocal = preInstr.getLocalSlot(); - if (info.nextReads[omitLocal] != null) - preInfo.local.combineInto(info.nextReads[omitLocal].local); - } - for (int i=0; i < maxlocals; i++) { - if (info.nextReads[i] != null && i != omitLocal - && (mergeSet == null || mergeSet.get(i) != inverted)) { - - if (preInfo.nextReads[i] == null) { - preInfo.nextReads[i] = info.nextReads[i]; - changedInfos.add(preInfo); - } else { - preInfo.nextReads[i].local - .combineInto(info.nextReads[i].local); - } - } - } - } - - void promoteReads(InstrInfo info, Instruction preInstr) { - promoteReads(info, preInstr, null, false); - } - - public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt, - int slot, int addr) { - LocalVariableInfo match = null; - for (int i=0; i < lvt.length; i++) { - if (lvt[i].slot == slot - && lvt[i].start.getAddr() <= addr - && lvt[i].end.getAddr() >= addr) { - if (match != null - && (!match.name.equals(lvt[i].name) - || !match.type.equals(lvt[i].type))) { - /* Multiple matches..., give no info */ - return null; - } - match = lvt[i]; - } - } - return match; - } - - public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt, - Instruction instr) { - int addr; - if (instr.getOpcode() >= opc_istore - && instr.getOpcode() <= opc_astore) - addr = instr.getNextAddr(); - else - addr = instr.getAddr(); - return findLVTEntry(lvt, instr.getLocalSlot(), addr); - } - public void calcLocalInfo() { - maxlocals = bc.getMaxLocals(); - Handler[] handlers = bc.getExceptionHandlers(); - LocalVariableInfo[] lvt = bc.getLocalVariableTable(); - if (lvt != null) - produceLVT = true; + maxlocals = bb.getMaxLocals(); + Block[] blocks = bb.getBlocks(); /* Initialize paramLocals */ { - String methodType = bc.getMethodInfo().getType(); - int paramCount = (bc.getMethodInfo().isStatic() ? 0 : 1) + String methodType = bb.getMethodInfo().getType(); + int paramCount = (bb.getMethodInfo().isStatic() ? 0 : 1) + TypeSignature.getArgumentSize(methodType); paramLocals = new LocalInfo[paramCount]; int slot = 0; - if (!bc.getMethodInfo().isStatic()) { + if (!bb.getMethodInfo().isStatic()) { LocalInfo local = new LocalInfo(); - if (lvt != null) { - LocalVariableInfo lvi = findLVTEntry(lvt, 0, 0); - if (lvi != null) { - local.name = lvi.name; - local.type = lvi.type; - } - } + LocalVariableInfo lvi = bb.getParamInfo(slot); + local.type = "L" + (bb.getMethodInfo().getClazzInfo() + .getName().replace('.', '/'))+";"; + if (local.type.equals(lvi.getType())) + local.name = lvi.getName(); local.size = 1; + local.id = " this"; paramLocals[slot++] = local; } int pos = 1; while (pos < methodType.length() && methodType.charAt(pos) != ')') { - - LocalInfo local = new LocalInfo(); - if (lvt != null) { - LocalVariableInfo lvi = findLVTEntry(lvt, slot, 0); - if (lvi != null) { - local.name = lvi.name; - } - } - int start = pos; pos = TypeSignature.skipType(methodType, pos); + + LocalInfo local = new LocalInfo(); + LocalVariableInfo lvi = bb.getParamInfo(slot); local.type = methodType.substring(start, pos); + if (local.type.equals(lvi.getType())) + local.name = lvi.getName(); local.size = TypeSignature.getTypeSize(local.type); + local.id = " parm"; paramLocals[slot] = local; slot += local.size; } @@ -364,97 +471,28 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { /* Initialize the InstrInfos and LocalInfos */ changedInfos = new TodoQueue(); - instrInfos = new Hashtable(); - { - InstrInfo info = firstInfo = new InstrInfo(); - Iterator i = bc.getInstructions().iterator(); - while (true) { - Instruction instr = (Instruction) i.next(); - instrInfos.put(instr, info); - info.instr = instr; - info.nextReads = new InstrInfo[maxlocals]; - if (instr.hasLocalSlot()) { - info.local = new LocalInfo(info); - if (lvt != null) { - LocalVariableInfo lvi = findLVTEntry(lvt, instr); - if (lvi != null) { - info.local.name = lvi.name; - info.local.type = lvi.type; - } - } - info.local.size = 1; - switch (instr.getOpcode()) { - case opc_lload: case opc_dload: - info.local.size = 2; - /* fall through */ - case opc_iload: case opc_fload: case opc_aload: - case opc_iinc: - /* this is a load instruction */ - info.nextReads[instr.getLocalSlot()] = info; - changedInfos.add(info); - break; + blockInfos = new BlockInfo[blocks.length]; + for (int i=0; i< blocks.length; i++) + blockInfos[i] = new BlockInfo(i, blocks[i]); - case opc_ret: - /* this is a ret instruction */ - info.usedBySub = new BitSet(); - info.nextReads[instr.getLocalSlot()] = info; - changedInfos.add(info); - break; - - case opc_lstore: case opc_dstore: - info.local.size = 2; - //case opc_istore: case opc_fstore: case opc_astore: - } - } - if (!i.hasNext()) - break; - info = info.nextInfo = new InstrInfo(); + for (int i=0; i< blocks.length; i++) { + int[] succs = blocks[i].getSuccs(); + for (int j=0; j< succs.length; j++) { + if (succs[j] >= 0) + blockInfos[succs[j]].preds.add(blockInfos[i]); + } + BasicBlocks.Handler[] handlers = blocks[i].getCatcher(); + for (int j=0; j< handlers.length; j++) { + blockInfos[handlers[j].getCatcher()] + .tryBlocks.add(blockInfos[i]); } } /* find out which locals are the same. */ while (!changedInfos.isEmpty()) { - InstrInfo info = changedInfos.remove(); - Instruction instr = info.instr; - - /* Mark the local as used in all ret instructions */ - if (instr.hasLocalSlot()) { - int slot = instr.getLocalSlot(); - for (int i=0; i< maxlocals; i++) { - InstrInfo retInfo = info.nextReads[i]; - if (retInfo != null - && retInfo.instr.getOpcode() == opc_ret - && !retInfo.usedBySub.get(slot)) { - retInfo.usedBySub.set(slot); - if (retInfo.jsrTargetInfo != null) - changedInfos.add(retInfo.jsrTargetInfo); - } - } - } - - Instruction prevInstr = instr.getPrevByAddr(); - if (prevInstr != null) { - if (!prevInstr.doesAlwaysJump()) - promoteReads(info, prevInstr); - else if (prevInstr.getOpcode() == opc_jsr) { - /* Prev instr is a jsr, promote reads to the - * corresponding ret. - */ - InstrInfo jsrInfo = - (InstrInfo) instrInfos.get(prevInstr.getSingleSucc()); - if (jsrInfo.retInfo != null) { - /* Now promote reads that are modified by the - * subroutine to the ret, and those that are not - * to the jsr instruction. - */ - promoteReads(info, jsrInfo.retInfo.instr, - jsrInfo.retInfo.usedBySub, false); - promoteReads(info, prevInstr, - jsrInfo.retInfo.usedBySub, true); - } - } - } + BlockInfo info = changedInfos.remove(); + info.promoteIns(); if (instr.getPreds() != null) { for (int i = 0; i < instr.getPreds().length; i++) { @@ -500,48 +538,45 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { promoteReads(info, instr.getPreds()[i]); } } - - for (int i=0; i < handlers.length; i++) { - if (handlers[i].catcher == instr) { - for (Instruction preInstr = handlers[i].start; - preInstr != handlers[i].end.getNextByAddr(); - preInstr = preInstr.getNextByAddr()) { - promoteReads(info, preInstr); - } - } - } } changedInfos = null; /* Now merge with the parameters * The params should be the locals in firstInfo.nextReads */ - for (int i=0; i< paramLocals.length; i++) { - if (firstInfo.nextReads[i] != null) { - firstInfo.nextReads[i].local.combineInto(paramLocals[i]); - paramLocals[i] = paramLocals[i].getReal(); + int startBlock = bb.getStartBlock(); + if (startBlock >= 0) { + LocalInfo[] ins = blockInfos[startBlock].ins; + for (int i=0; i< paramLocals.length; i++) { + if (ins[i] != null) + paramLocals[i].combineInto(ins[i]); } } } public void stripLocals() { - ListIterator iter = bc.getInstructions().listIterator(); - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { - Instruction instr = (Instruction) iter.next(); - if (info.local != null && info.local.usingInstrs.size() == 1) { - /* If this is a store, whose value is never read; it can - * be removed, i.e replaced by a pop. */ - switch (instr.getOpcode()) { - case opc_istore: - case opc_fstore: - case opc_astore: - iter.set(new Instruction(opc_pop)); - break; - case opc_lstore: - case opc_dstore: - iter.set(new Instruction(opc_pop2)); - break; - default: + Block[] blocks = bb.getBlocks(); + for (int i = 0; i < blocks.length; i++) { + Instruction[] instrs = blocks[i].getInstructions(); + for (int j = 0; j < instrs.length; j++) { + Instruction instr = instrs[j]; + if (info.local != null + && info.local.usingBlocks.size() == 1) { + /* If this is a store, whose value is never read; it can + * be removed, i.e replaced by a pop. + */ + switch (instr.getOpcode()) { + case opc_istore: + case opc_fstore: + case opc_astore: + instrs[j] = Instruction.forOpcode(opc_pop); + break; + case opc_lstore: + case opc_dstore: + instrs[j] = Instruction.forOpcode(opc_pop2); + break; + default: + } } } } @@ -611,38 +646,8 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { /* Now calculate the conflict settings. */ - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { - if (info.instr.getOpcode() >= BytecodeInfo.opc_istore - && info.instr.getOpcode() <= BytecodeInfo.opc_astore) { - /* This is a store. It conflicts with every local, whose - * value will be read without write. - * - * If this is inside a ret, it also conflicts with - * locals, that are not used inside, and where any jsr - * would conflict with. - */ - for (int i=0; i < maxlocals; i++) { - if (i != info.instr.getLocalSlot() - && info.nextReads[i] != null) - info.local.conflictsWith(info.nextReads[i].local); - if (info.nextInfo.nextReads[i] != null - && info.nextInfo.nextReads[i].jsrTargetInfo != null) { - Instruction[] jsrs = info.nextInfo.nextReads[i] - .jsrTargetInfo.instr.getPreds(); - for (int j=0; j< jsrs.length; j++) { - InstrInfo jsrInfo - = (InstrInfo) instrInfos.get(jsrs[j]); - for (int k=0; k < maxlocals; k++) { - if (!info.nextInfo.nextReads[i].usedBySub - .get(k) - && jsrInfo.nextReads[k] != null) - info.local.conflictsWith - (jsrInfo.nextReads[k].local); - } - } - } - } - } + for (int blockNr = 0; blockNr < blockInfos.length; blockNr++) { + blockInfos[blockNr].generateConflicts(); } /* Now put the locals that need a color into a vector. @@ -670,11 +675,6 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { } } bc.setMaxLocals(maxlocals); - - /* Update LocalVariableTable - */ - if (produceLVT) - buildNewLVT(); } private InstrInfo CONFLICT = new InstrInfo(); @@ -704,222 +704,63 @@ public class LocalOptimizer implements Opcodes, CodeTransformer { return changed; } - public void buildNewLVT() { - /* First we recalculate the usedBySub, to use the new local numbers. - */ - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) - if (info.usedBySub != null) - info.usedBySub = new BitSet(); - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { - if (info.local != null) { - for (int i=0; i < info.nextReads.length; i++) { - if (info.nextReads[i] != null - && info.nextReads[i].instr.getOpcode() == opc_ret) - info.nextReads[i].usedBySub.set(info.local.newSlot); - } - } - } - - /* Now we begin with the first Instruction and follow program flow. - * We remember which locals are life in lifeLocals. - */ - - firstInfo.lifeLocals = new LocalInfo[maxlocals]; - for (int i=0; i < paramLocals.length; i++) - firstInfo.lifeLocals[i] = paramLocals[i]; - - Stack changedInfo = new Stack(); - changedInfo.push(firstInfo); - Handler[] handlers = bc.getExceptionHandlers(); - while (!changedInfo.isEmpty()) { - InstrInfo info = (InstrInfo) changedInfo.pop(); - Instruction instr = info.instr; - LocalInfo[] newLife = info.lifeLocals; - if (instr.hasLocalSlot()) { - int slot = instr.getLocalSlot(); - LocalInfo instrLocal = info.local.getReal(); - newLife = (LocalInfo[]) newLife.clone(); - newLife[slot] = instrLocal; - if (instrLocal.name != null) { - for (int j=0; j< newLife.length; j++) { - if (j != slot - && newLife[j] != null - && instrLocal.name.equals(newLife[j].name)) { - /* This local changed the slot. */ - newLife[j] = null; - } - } - } - } - - if (!instr.doesAlwaysJump()) { - InstrInfo nextInfo = info.nextInfo; - if (promoteLifeLocals(newLife, nextInfo)) - changedInfo.push(nextInfo); - } - if (instr.hasSuccs()) { - Instruction[] succs = instr.getSuccs(); - for (int i = 0; i < succs.length; i++) { - InstrInfo nextInfo - = (InstrInfo) instrInfos.get(succs[i]); - if (promoteLifeLocals(newLife, nextInfo)) - changedInfo.push(nextInfo); - } - } - for (int i=0; i < handlers.length; i++) { - if (handlers[i].start.compareTo(instr) <= 0 - && handlers[i].end.compareTo(instr) >= 0) { - InstrInfo nextInfo - = (InstrInfo) instrInfos.get(handlers[i].catcher); - if (promoteLifeLocals(newLife, nextInfo)) - changedInfo.push(nextInfo); - } - } - - if (info.instr.getOpcode() == opc_jsr) { - /* On a jsr we do a special merge */ - - Instruction jsrTargetInstr = info.instr.getSingleSucc(); - InstrInfo jsrTargetInfo - = (InstrInfo) instrInfos.get(jsrTargetInstr); - InstrInfo retInfo = jsrTargetInfo.retInfo; - if (retInfo != null && retInfo.lifeLocals != null) { - LocalInfo[] retLife = (LocalInfo[]) newLife.clone(); - for (int i=0; i< maxlocals; i++) { - if (retInfo.usedBySub.get(i)) - retLife[i] = retInfo.lifeLocals[i]; - } - if (promoteLifeLocals(retLife, info.nextInfo)) - changedInfo.push(info.nextInfo); - } - } - - if (info.jsrTargetInfo != null) { - /* On a ret we do a special merge */ - - Instruction jsrTargetInstr = info.jsrTargetInfo.instr; - for (int j=0; j< jsrTargetInstr.getPreds().length; j++) { - InstrInfo jsrInfo - = (InstrInfo) instrInfos.get(jsrTargetInstr.getPreds()[j]); - - if (jsrInfo.lifeLocals == null) - /* life locals are not calculated, yet */ - continue; - LocalInfo[] retLife = (LocalInfo[]) newLife.clone(); - for (int i=0; i< maxlocals; i++) { - if (!info.usedBySub.get(i)) - retLife[i] = jsrInfo.lifeLocals[i]; - } - if (promoteLifeLocals(retLife, jsrInfo.nextInfo)) - changedInfo.push(jsrInfo.nextInfo); - } - } - } - - Vector lvtEntries = new Vector(); - LocalVariableInfo[] lvi = new LocalVariableInfo[maxlocals]; - LocalInfo[] currentLocal = new LocalInfo[maxlocals]; - for (int i=0; i< paramLocals.length; i++) { - if (paramLocals[i] != null) { - currentLocal[i] = paramLocals[i]; - if (currentLocal[i].name != null) { - lvi[i] = new LocalVariableInfo(); - lvtEntries.addElement(lvi[i]); - lvi[i].name = currentLocal[i].name; /* XXX obfuscation? */ - lvi[i].type = Main.getClassBundle() - .getTypeAlias(currentLocal[i].type); - lvi[i].start = (Instruction) bc.getInstructions().get(0); - lvi[i].slot = i; - } - } - } - Instruction lastInstr = null; - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { - for (int i=0; i< maxlocals; i++) { - LocalInfo lcl = info.lifeLocals != null ? info.lifeLocals[i] - : null; - if (lcl != currentLocal[i] - && (lcl == null || currentLocal[i] == null - || lcl.name == null || lcl.type == null - || !lcl.name.equals(currentLocal[i].name) - || !lcl.type.equals(currentLocal[i].type))) { - if (lvi[i] != null) { - lvi[i].end = info.instr.getPrevByAddr(); - } - lvi[i] = null; - currentLocal[i] = lcl; - if (currentLocal[i] != null - && currentLocal[i].name != null - && currentLocal[i].type != null) { - lvi[i] = new LocalVariableInfo(); - lvtEntries.addElement(lvi[i]); - lvi[i].name = currentLocal[i].name; - lvi[i].type = Main.getClassBundle() - .getTypeAlias(currentLocal[i].type); - lvi[i].start = info.instr; - lvi[i].slot = i; - } - } - } - lastInstr = info.instr; - } - for (int i=0; i< maxlocals; i++) { - if (lvi[i] != null) - lvi[i].end = lastInstr; - } - LocalVariableInfo[] lvt = new LocalVariableInfo[lvtEntries.size()]; - lvtEntries.copyInto(lvt); - bc.setLocalVariableTable(lvt); - } - public void dumpLocals() { Vector locals = new Vector(); - for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { - GlobalOptions.err.println(info.instr.getDescription()); - GlobalOptions.err.print("nextReads: "); - for (int i=0; i> 5] & (1 << (pos & 31))) != 0; + } + + boolean isRemoved(int pos) { + return (removedInstrs[pos >> 5] & (1 << (pos & 31))) != 0; + } + } + + public BlockInfo analyzeBlock(Block block, BlockInfo oldInfo) { + } + + public BlockInfo[] calcBlockInfos(BasicBlocks bb) { + Block[] blocks = bb.getBlocks(); + BlockInfo[] infos = new BlockInfo[blocks.length]; int poppush[] = new int[2]; - ListIterator iter = bytecode.getInstructions().listIterator(); - next_pop: - while (iter.hasNext()) { - Instruction popInstr = (Instruction) iter.next(); - boolean isPop2 = false; - switch (popInstr.getOpcode()) { - case opc_nop: { - iter.remove(); - continue; - } + int maxStack = bb.getMaxStack(); + int poppedLen = maxStack >> 5; - case opc_pop2: - isPop2 = true; - case opc_pop: - if (popInstr.getPreds() != null) - // Can't handle pop with multiple predecessors - continue next_pop; - Handler[] handlers = bytecode.getExceptionHandlers(); - for (int i=0; i < handlers.length; i++) - if (handlers[i].catcher == popInstr) - continue next_pop; - - // remove pop, we will insert it again if something - // bad happened. - iter.remove(); - - // remember position of pop, so we can insert it again. - Instruction popPrevious = (Instruction) iter.previous(); - Instruction instr = popPrevious; - int count = 0; - while (true) { - if (instr.getSuccs() != null - || instr.doesAlwaysJump()) { - instr = null; - break; - } - instr.getStackPopPush(poppush); + for (int i = 0; i < blocks.length; i++) { + BitSet popped = new BitSet(); + Instruction[] instrs = blocks[i].getInstructions(); + int[] removed = instrs.length >> 5; - if (count < poppush[1]) { - if (count == 0) - break; + // Be conservative with stack depth at end of block + int depth = maxStack; - int opcode = instr.getOpcode(); - /* If this is a dup and the instruction popped is the - * duplicated element, remove the dup and the pop - */ - if (count <= 3 && opcode == (opc_dup + count - 1)) { - iter.remove(); - if (!isPop2) - continue next_pop; - - // We have to consider a pop instead of a - // pop2 now. - popInstr = new Instruction(opc_pop); - isPop2 = false; - instr = (Instruction) iter.previous(); - continue; - } - - if (isPop2 - && count > 1 && count <= 4 - && opcode == (opc_dup2 + count-2)) { - iter.remove(); - continue next_pop; - } - /* Otherwise popping is not possible */ - instr = null; - break; + for (int j = instrs.length; j-- > 0; ) { + Instruction instr = instrs[j]; + + instr.getStackPopPush(poppush); + + // Now check if the parameters of this instr are needed. + boolean params_needed = false; + switch (instr.getOpcode()) { + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_checkcast: + /* These instructions have side effects, parameters + * are always needed. Adjust depth. + */ + params_needed = true; + depth += poppush[1]; + break; + + default: + /* Check if all results are needed, adjust depth */ + for (int k = 0; k < poppush[1]; k++) { + if (!popped.get(depth++)) + params_needed = true; } - count += poppush[0] - poppush[1]; - instr = (Instruction) iter.previous(); } - if (instr == null) { - // We insert the pop at the previous position - while (iter.next() != popPrevious) - {} - if (!isPop2 && popPrevious.getOpcode() == opc_pop) { - // merge pop with popPrevious - iter.set(new Instruction(opc_pop2)); - } else - iter.add(popInstr); - continue; + if (params_needed) { + /* mark params as needed */ + for (int k = 0; k < poppush[0]; k++) + popped.clear(--depth); + } else { + removed[j >> 5] |= 1 << (j & 31); + /* mark params as popped */ + for (int k = 0; k < poppush[0]; k++) + popped.set(--depth); + } + } + + int[] poppedArr = new int[poppedLen]; + if (blocks[i] != bb.getStartBlock()) { + /* Only can pop entries before this block if + * this isn't the start block. + */ + for (int k = 0; k < block_pop; k++) { + if (popped.get(depth+k)) + poppedArr[k >> 5] |= 1 << (k & 31); } - int opcode = instr.getOpcode(); - switch (opcode) { - case opc_ldc2_w: - case opc_lload: case opc_dload: - if (!isPop2) - throw new AssertError("pop on long"); - iter.remove(); + } + infos[i] = new BlockInfo(poppedArr, removed); + } + + /* Now start sharing poppedEntries as necessary. */ + int[] empty = new int[poppedLen]; + next_block: + for (int i = 1; i < blocks.length; i++) { + /* Try to find an earlier block with a same predecessor. */ + for (int j = 0; j < blocks.length; j++) { + Block[] succs = blocks[j].getSuccs(); + if (succs.length < 2) continue; - case opc_ldc: - case opc_iload: case opc_fload: case opc_aload: - case opc_dup: - case opc_new: - if (isPop2) - iter.set(new Instruction(opc_pop)); - else - iter.remove(); + + int blockNr = -1; + boolean isAPred = false; + for (int k = 0; k < succs.length; k++) { + if (succs[k] == blocks[i]) + isAPred = true; + if (succs[k] != null && succs[k].getBlockNr() < i) + blockNr = succs[k].getBlockNr(); + if (isAPred && blockNr >= 0) { + int[] common = infos[blockNr].poppedEntries; + int[] my = infos[i].poppedEntries; + for (int k = 0; k < poppedLen; k++) + common[k] &= my[k]; + infos[i].poppedEntries = common; + continue next_block; + } + } + } + } + + /* Now propagate poppedEntries through blocks */ + boolean changed = true; + while (changed) { + changed = false; + next_block: + for (int i = 0; i < blocks.length; i++) { + Block[] succs = blocks[i].getSuccs(); + int[] succPops = null; + for (int j = 0; j < succs.length; j++) { + if (succs[j] != null) { + succPops = infos[succs[j].getBlockNr()].poppedEntries; + } + } + if (succPops == null) continue; - case opc_iaload: case opc_faload: case opc_aaload: - case opc_baload: case opc_caload: case opc_saload: - case opc_iadd: case opc_fadd: - case opc_isub: case opc_fsub: - case opc_imul: case opc_fmul: - case opc_idiv: case opc_fdiv: - case opc_irem: case opc_frem: - case opc_iand: case opc_ior : case opc_ixor: - case opc_ishl: case opc_ishr: case opc_iushr: - case opc_fcmpl: case opc_fcmpg: - /* We have to pop one entry more. */ - iter.next(); - iter.add(popInstr); - iter.previous(); - iter.previous(); - iter.set(new Instruction(opc_pop)); + blocks[i].getStackPopPush(poppush); + int[] myPops = infos[i].poppedPush; + for (int k = poppush[1]; k < maxStack; k++) { + + if (succs.length == 0 || succs[0] == null) continue; + int[] succsPoppedEntries + = infos[succs[0].getBlockNr()].poppedEntries; + for (int j = 0; j < succs.length; j++) { + if (succs[j] == null) + continue next_block; + int[] thisPopped + = infos[succs[j].getBlockNr()].poppedEntries; - case opc_dup_x1: - iter.set(new Instruction(opc_swap)); - iter.next(); - if (isPop2) - iter.add(new Instruction(opc_pop)); - continue; + for (int k = 0; k < poppedLen; k++) { + succsPoppedEntries &= - case opc_dup2: - if (isPop2) { - iter.remove(); - continue; - } - break; - case opc_swap: - if (isPop2) { - iter.set(popInstr); - continue; - } + + for (int j = instrs.length; j-- > 0; ) { + Instruction instr = instrs[j]; + + instr.getStackPopPush(poppush); + + // Now check if the parameters of this instr are needed. + boolean params_needed = false; + switch (instr.getOpcode()) { + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_checkcast: + /* These instructions have side effects, parameters + * are always needed. Adjust depth. + */ + params_needed = true; + depth += poppush[1]; break; - case opc_lneg: case opc_dneg: - case opc_l2d: case opc_d2l: - case opc_laload: case opc_daload: - if (!isPop2) - throw new AssertError("pop on long"); - /* fall through */ - case opc_ineg: case opc_fneg: - case opc_i2f: case opc_f2i: - case opc_i2b: case opc_i2c: case opc_i2s: - case opc_newarray: case opc_anewarray: - case opc_arraylength: - case opc_instanceof: - iter.set(popInstr); - continue; + case opc_pop: + case opc_pop2: + break; - case opc_l2i: case opc_l2f: - case opc_d2i: case opc_d2f: - if (isPop2) { - iter.next(); - iter.add(new Instruction(opc_pop)); - iter.previous(); - iter.previous(); + default: + /* Check if all results are needed, adjust depth */ + for (int k = 0; k < poppush[1]; k++) { + if (!popped.get(depth++)) + params_needed = true; } - iter.set(new Instruction(opc_pop2)); - continue; + /* If opcode has no result it has side effects. */ + if (poppush[1] == 0) + params_needed = true; + } + if (block_pop < maxStack - depth) + block_pop = maxStack - depth; - case opc_ladd: case opc_dadd: - case opc_lsub: case opc_dsub: - case opc_lmul: case opc_dmul: - case opc_ldiv: case opc_ddiv: - case opc_lrem: case opc_drem: - case opc_land: case opc_lor : case opc_lxor: - if (!isPop2) - throw new AssertError("pop on long"); - iter.next(); - iter.add(popInstr); - iter.previous(); - iter.previous(); - iter.set(new Instruction(opc_pop2)); - continue; - case opc_lshl: case opc_lshr: case opc_lushr: - if (!isPop2) - throw new AssertError("pop on long"); - iter.next(); - iter.add(popInstr); - iter.previous(); - iter.previous(); - iter.set(new Instruction(opc_pop)); - continue; + if (params_needed) { + /* mark params as needed */ + for (int k = 0; k < poppush[0]; k++) + popped.clear(--depth); + } else { + removed[j >> 5] |= 1 << (j & 31); + /* mark params as popped */ + for (int k = 0; k < poppush[0]; k++) + popped.set(--depth); + } + } - case opc_i2l: case opc_i2d: - case opc_f2l: case opc_f2d: - if (!isPop2) - throw new AssertError("pop on long"); - iter.set(new Instruction(opc_pop)); + if (blocks[j] == null) + ; + } + } + } + return infos; + } + + public void transformCode(BasicBlocks bb) { + if (bb.getStartBlock() == null) + return; + + BlockInfo[] infos = calcBlockInfos(bb); + + int poppush[] = new int[2]; + boolean poppedEntries[] = new boolean[bb.getMaxStack()]; + Block[] blocks = bb.getBlocks(); + for (int i = 0; i < blocks.length; i++) { + LinkedList newInstructions = new LinkedList(); + Instruction[] oldInstrs = blocks[i].getInstructions(); + int instrNr = oldInstrs.length; + int stackDepth = 0; + while (instrNr > 0) { + Instruction instr = oldInstrs[instrNr]; + if (instr.getOpcode() == opc_nop) continue; - - case opc_lcmp: - case opc_dcmpl: case opc_dcmpg: - iter.next(); - iter.add(new Instruction(opc_pop2)); - if (isPop2) { - iter.add(new Instruction(opc_pop)); - iter.previous(); - } - iter.previous(); - iter.previous(); - iter.set(new Instruction(opc_pop2)); + if (instr.getOpcode() == opc_pop) { + poppedEntries[stackDepth++] = true; continue; - - case opc_getstatic: - case opc_getfield: { - Reference ref = instr.getReference(); - int size = TypeSignature.getTypeSize(ref.getType()); - if (size == 2 && !isPop2) - throw new AssertError("pop on long"); - if (opcode == opc_getfield) - size--; - switch (size) { - case 0: - iter.set(popInstr); - break; - case 1: - if (isPop2) { - iter.set(new Instruction(opc_pop)); - break; - } - /* fall through */ - case 2: - iter.remove(); - } + } + if (instr.getOpcode() == opc_pop2) { + poppedEntries[stackDepth++] = true; + poppedEntries[stackDepth++] = true; continue; } - case opc_multianewarray: { - int dims = instr.getDimensions(); - if (--dims > 0) { - iter.next(); - while (dims-- > 0) { - iter.add(new Instruction(opc_pop)); - iter.previous(); - } - iter.previous(); - } - iter.set(popInstr); - continue; + instr.getStackPopPush(poppush); + // First look if stackDepth was right + if (stackDepth < poppush[1]) { + int diff = poppush[1] - stackDepth; + System.arraycopy(poppedEntries, 0, + poppedEntries, diff, stackDepth); + for (int j=0; j< diff; i++) + poppedEntries[j] = false; + } + // Now check if this instr pushes a popped Entry. + boolean push_a_popped = false; + boolean push_all_popped = true; + for (int j=0; j < poppush[1]; j++) { + if (poppedEntries[j]) + push_a_popped = true; + else + push_all_popped = false; } + if (push_a_popped) { + /* We push an entry, that gets popped later */ + int opcode = instr.getOpcode(); + switch (opcode) { + case opc_dup: + case opc_dup_x1: + case opc_dup_x2: + case opc_dup2: + case opc_dup2_x1: + case opc_dup2_x2: { - case opc_invokevirtual: - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: - if (TypeSignature.getReturnSize - (instr.getReference().getType()) != 1) + int count = (opcode - opc_dup)/3+1; + int depth = (opcode - opc_dup)%3; + stackDepth -= count; + int bottom = stackDepth - count - depth; + + int popped1 = 0; + int popped3 = 0; + int newDepth = 0; + + // get the popped mask and adjust poppedEntries. + for (int j=0; j< count; j++) { + if (poppedEntries[bottom + j]) + popped1 |= 1<>= 1; + popped3 >>= 2; + } else if ((popped1 & popped3 & 2) != 0) { + count--; + popped1 &= 1; + popped3 &= 1; + } + + if (popped1 == 1) { + // count == 2, popped1 = 1, + depth++; + count--; + popped1 = 0; + popped3 >>= 1; + } + if (count == 2 && popped1 == 0 && popped3 > 0) { + // Do the normal dup2 and pop the right + // element afterwards. + if (popped3 == 3) { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop2)); + } else { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); + if (popped3 == 1) + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); + } + popped3 = 0; + } + if (popped1 == popped3) { + // popped1 == popped3 == 0 + // Do a dupcount_xdepth now. + if (depth < 3) { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop - 3 + + depth + 3 * count)); + break; + } else { + // I hope that this will almost never happen. + // depth = 3, count = 1; + // Note that instructions are backwards. + newInstructions.addFirst + (Instruction.forOpcode(opc_pop2)); //DABCD + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x2)); //DABCDAB + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); //DCDAB + newInstructions.addFirst + (Instruction.forOpcode(opc_dup_x2)); //DCDABD + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); //DCABD + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x2)); //DCABDC + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); //ABDC + break; + } + } + + if (count == 1) { + // Possible states: + // popped1 = 0; popped3 = 1; + // depth = 1 or depth = 2 + if (depth == 1) { + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); + } else { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); + newInstructions.addFirst(instr); + } + break; + } + + // count = 2; popped1 = 2 + // Possible states: + // dpth/pop3 0 1 + // 0 AB AAB AB + // 1 ABC BABC BAC + // 2 ABCD CABCD CABD + if (popped3 == 0) { + if (depth == 0) { + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); + newInstructions.addFirst + (Instruction.forOpcode(opc_dup_x1)); + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); + } else if (depth == 1) { + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); //BABC + newInstructions.addFirst + (Instruction.forOpcode(opc_dup_x2)); //BACB + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); //ACB + } else { + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); //CABCD + newInstructions.addFirst + (Instruction.forOpcode(opc_pop2)); //CABDC + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x2)); //CABDCAB + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); //CDCAB + newInstructions.addFirst + (Instruction.forOpcode(opc_dup_x2)); //CDCABC + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); //CDABC + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x2)); //CDABCD + } + } else { + if (depth == 0) { + } else if (depth == 1) { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop)); //BAC + newInstructions.addFirst + (Instruction.forOpcode(opc_dup_x2)); //BACB + newInstructions.addFirst + (Instruction.forOpcode(opc_swap)); //ACB + } else { + newInstructions.addFirst + (Instruction.forOpcode(opc_pop2)); //CABD + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x1)); //CABDAB + newInstructions.addFirst + (Instruction.forOpcode(opc_pop2)); //CDAB + newInstructions.addFirst + (Instruction.forOpcode(opc_dup2_x2)); //CDABCD + } + } break; - /* fall through */ - case opc_checkcast: - if (isPop2) { - /* This is/may be a double pop on a single value - * split it and continue with second half - */ - iter.next(); - iter.add(new Instruction(opc_pop)); - iter.add(new Instruction(opc_pop)); - iter.previous(); + } + case opc_swap: { + // swap the popped status + boolean tmp = poppedEntries[stackDepth - 1]; + poppedEntries[stackDepth - 1] + = poppedEntries[stackDepth - 2]; + poppedEntries[stackDepth - 2] = tmp; + } + + + // Now the simple instructions, that can be removed. + // delta = -2; + case opc_ldc2_w: + case opc_lload: case opc_dload: + if (!push_all_popped) + throw new AssertError("pop half of a long"); + poppedEntries[--stackDepth] = false; + poppedEntries[--stackDepth] = false; continue; + + case opc_i2l: case opc_i2d: + case opc_f2l: case opc_f2d: + case opc_ldc: + case opc_iload: case opc_fload: case opc_aload: + case opc_new: + case opc_lneg: case opc_dneg: + case opc_l2d: case opc_d2l: + case opc_laload: case opc_daload: + case opc_ineg: case opc_fneg: + case opc_i2f: case opc_f2i: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_newarray: case opc_anewarray: + case opc_arraylength: + case opc_instanceof: + case opc_lshl: case opc_lshr: case opc_lushr: + case opc_iaload: case opc_faload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + case opc_iadd: case opc_fadd: + case opc_isub: case opc_fsub: + case opc_imul: case opc_fmul: + case opc_idiv: case opc_fdiv: + case opc_irem: case opc_frem: + case opc_iand: case opc_ior : case opc_ixor: + case opc_ishl: case opc_ishr: case opc_iushr: + case opc_fcmpl: case opc_fcmpg: + case opc_l2i: case opc_l2f: + case opc_d2i: case opc_d2f: + case opc_ladd: case opc_dadd: + case opc_lsub: case opc_dsub: + case opc_lmul: case opc_dmul: + case opc_ldiv: case opc_ddiv: + case opc_lrem: case opc_drem: + case opc_land: case opc_lor : case opc_lxor: + case opc_lcmp: + case opc_dcmpl: case opc_dcmpg: + case opc_getstatic: + case opc_getfield: + case opc_multianewarray: + + if (!push_all_popped) + throw new AssertError("pop half of a long"); + if (poppush[0] < poppush[1]) { + for (int j=0; j < poppush[0] - poppush[1]; j++) + poppedEntries[stackDepth++] = true; + } else if (poppush[0] < poppush[1]) { + for (int j=0; j < poppush[0] - poppush[1]; j++) + poppedEntries[--stackDepth] = false; + } + + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_checkcast: + if (!push_all_popped) + throw new AssertError("pop half of a long"); + if (poppush[1] == 1) { + poppedEntries[--stackDepth] = false; + newInstructions + .addFirst(Instruction.forOpcode(opc_pop)); + } else { + poppedEntries[--stackDepth] = false; + poppedEntries[--stackDepth] = false; + newInstructions + .addFirst(Instruction.forOpcode(opc_pop2)); + } + newInstructions.addFirst(instr); + default: + throw new AssertError("Unexpected opcode!"); } + } else { + // Add the instruction .. + newInstructions.addFirst(instr); + // .. and adjust stackDepth. + stackDepth += poppush[0] - poppush[1]; } - // append the pop behind the unresolvable opcode. - iter.next(); - iter.add(popInstr); - continue; } + for (int j=0; j < stackDepth; j++) { + // XXXX + } + blocks[i].setCode((Instruction[]) newInstructions + .toArray(oldInstrs), blocks[i].getSuccs()); } } } diff --git a/jode/jode/obfuscator/modules/SimpleAnalyzer.java b/jode/jode/obfuscator/modules/SimpleAnalyzer.java index 675f6be..9d5c190 100644 --- a/jode/jode/obfuscator/modules/SimpleAnalyzer.java +++ b/jode/jode/obfuscator/modules/SimpleAnalyzer.java @@ -18,10 +18,11 @@ */ package jode.obfuscator.modules; -import jode.bytecode.Handler; import jode.bytecode.Opcodes; import jode.bytecode.ClassInfo; -import jode.bytecode.BytecodeInfo; +import jode.bytecode.BasicBlocks; +import jode.bytecode.Block; +import jode.bytecode.Handler; import jode.bytecode.Instruction; import jode.bytecode.Reference; import jode.bytecode.TypeSignature; @@ -30,6 +31,7 @@ import jode.obfuscator.*; import jode.GlobalOptions; ///#def COLLECTIONS java.util +import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; ///#enddef @@ -93,94 +95,108 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { * references * @return an enumeration of the references. */ - public void analyzeCode(MethodIdentifier m, BytecodeInfo bytecode) { - for (Iterator iter = bytecode.getInstructions().iterator(); - iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - switch (instr.getOpcode()) { - case opc_checkcast: - case opc_instanceof: - case opc_multianewarray: { - String clName = instr.getClazzType(); - int i = 0; - while (i < clName.length() && clName.charAt(i) == '[') - i++; - if (i < clName.length() && clName.charAt(i) == 'L') { - clName = clName.substring(i+1, clName.length()-1) - .replace('/','.'); - Main.getClassBundle().reachableClass(clName); + public void analyzeCode(MethodIdentifier m, BasicBlocks bb) { + Block[] blocks = bb.getBlocks(); + for (int i=0; i < blocks.length; i++) { + Instruction[] instrs = blocks[i].getInstructions(); + for (int idx = 0; idx < instrs.length; idx++) { + int opcode = instrs[idx].getOpcode(); + switch (opcode) { + case opc_checkcast: + case opc_instanceof: + case opc_multianewarray: { + String clName = instrs[idx].getClazzType(); + int k = 0; + while (k < clName.length() && clName.charAt(k) == '[') + k++; + if (k < clName.length() && clName.charAt(k) == 'L') { + clName = clName.substring(k+1, clName.length()-1) + .replace('/','.'); + Main.getClassBundle().reachableClass(clName); + } + break; } - break; - } - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: - case opc_invokevirtual: - case opc_putstatic: - case opc_putfield: - m.setGlobalSideEffects(); - /* fall through */ - case opc_getstatic: - case opc_getfield: { - Identifier ident = canonizeReference(instr); - if (ident != null) { - if (instr.getOpcode() == opc_putstatic - || instr.getOpcode() == opc_putfield) { - FieldIdentifier fi = (FieldIdentifier) ident; - if (!fi.isNotConstant()) - fi.setNotConstant(); - } else if (instr.getOpcode() == opc_invokevirtual - || instr.getOpcode() == opc_invokeinterface) { - ((ClassIdentifier) ident.getParent()) - .reachableReference(instr.getReference(), true); - } else { - ident.setReachable(); + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: + case opc_putstatic: + case opc_putfield: + m.setGlobalSideEffects(); + /* fall through */ + case opc_getstatic: + case opc_getfield: { + Identifier ident = canonizeReference(instrs[idx]); + if (ident != null) { + if (opcode == opc_putstatic + || opcode == opc_putfield) { + FieldIdentifier fi = (FieldIdentifier) ident; + if (!fi.isNotConstant()) + fi.setNotConstant(); + } else if (opcode == opc_invokevirtual || + opcode == opc_invokeinterface) { + ((ClassIdentifier) ident.getParent()) + .reachableReference + (instrs[idx].getReference(), true); + } else { + ident.setReachable(); + } } + break; + } } - break; - } } } - Handler[] handlers = bytecode.getExceptionHandlers(); + Handler[] handlers = bb.getExceptionHandlers(); for (int i=0; i< handlers.length; i++) { - if (handlers[i].type != null) + if (handlers[i].getType() != null) Main.getClassBundle() - .reachableClass(handlers[i].type); + .reachableClass(handlers[i].getType()); } } - public void transformCode(BytecodeInfo bytecode) { - for (ListIterator iter = bytecode.getInstructions().listIterator(); - iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - if (instr.getOpcode() == opc_putstatic - || instr.getOpcode() == opc_putfield) { - Reference ref = instr.getReference(); - FieldIdentifier fi = (FieldIdentifier) - Main.getClassBundle().getIdentifier(ref); - if (fi != null - && (Main.stripping & Main.STRIP_UNREACH) != 0 - && !fi.isReachable()) { - /* Replace instruction with pop opcodes. */ - int stacksize = - (instr.getOpcode() - == Instruction.opc_putstatic) ? 0 : 1; - stacksize += TypeSignature.getTypeSize(ref.getType()); - switch (stacksize) { - case 1: - iter.set(new Instruction(Instruction.opc_pop)); - break; - case 2: - iter.set(new Instruction(Instruction.opc_pop2)); - break; - case 3: - iter.set(new Instruction(Instruction.opc_pop2)); - iter.add(new Instruction(Instruction.opc_pop)); - break; + public void transformCode(BasicBlocks bb) { + Block[] blocks = bb.getBlocks(); + for (int i=0; i < blocks.length; i++) { + Instruction[] instrs = blocks[i].getInstructions(); + ArrayList newCode = new ArrayList(); + Block[] newSuccs = blocks[i].getSuccs(); + for (int idx = 0; idx < instrs.length; idx++) { + int opcode = instrs[idx].getOpcode(); + if (opcode == opc_putstatic || opcode == opc_putfield) { + Reference ref = instrs[idx].getReference(); + FieldIdentifier fi = (FieldIdentifier) + Main.getClassBundle().getIdentifier(ref); + if (fi != null + && (Main.stripping & Main.STRIP_UNREACH) != 0 + && !fi.isReachable()) { + /* Replace instruction with pop opcodes. */ + int stacksize = + (opcode == Instruction.opc_putstatic) ? 0 : 1; + stacksize += TypeSignature.getTypeSize(ref.getType()); + switch (stacksize) { + case 1: + newCode.add(Instruction.forOpcode + (Instruction.opc_pop)); + continue; + case 2: + newCode.add(Instruction.forOpcode + (Instruction.opc_pop2)); + continue; + case 3: + newCode.add(Instruction.forOpcode + (Instruction.opc_pop2)); + newCode.add(Instruction.forOpcode + (Instruction.opc_pop)); + continue; + } } } + newCode.add(instrs[idx]); } + blocks[i].setCode((Instruction []) newCode.toArray(instrs), + newSuccs); } } } diff --git a/jode/jode/type/ArrayType.java b/jode/jode/type/ArrayType.java index 6743c0b..6b89e43 100644 --- a/jode/jode/type/ArrayType.java +++ b/jode/jode/type/ArrayType.java @@ -27,22 +27,38 @@ import java.io.IOException; * * @author Jochen Hoenicke */ -public class ArrayType extends ReferenceType { - // The interfaces that an array implements: - final static ClassInfo[] arrayIfaces = { - // Make sure to list all interfaces, even if some interface - // implements another (or change code in getGeneralizedType(). - ClassInfo.forName("java.lang.Cloneable"), - ClassInfo.forName("java.io.Serializable") - }; - +public class ArrayType extends ClassType { Type elementType; ArrayType(Type elementType) { - super(TC_ARRAY); + super(TC_ARRAY, elementType + "[]"); this.elementType = elementType; } + public boolean isInterface() { + return false; + } + + public boolean isUnknown() { + if (elementType instanceof ClassType) + return ((ClassType) elementType).isUnknown(); + return false; + } + + public boolean isFinal() { + if (elementType instanceof ClassType) + return ((ClassType) elementType).isFinal(); + return false; + } + + public ClassType getSuperClass() { + return tObject; + } + + public ClassType[] getInterfaces() { + return arrayIfaces; + } + public Type getElementType() { return elementType; } @@ -64,6 +80,14 @@ public class ArrayType extends ReferenceType { return tArray(elementType.getCanonic()); } + public boolean isSubTypeOf(Type type) { + if (type == tNull) + return true; + if (type instanceof ArrayType) + return elementType.isSubTypeOf(((ArrayType) type).elementType); + return false; + } + /** * Create the type corresponding to the range from bottomType to this. * @param bottomType the start point of the range @@ -75,22 +99,11 @@ public class ArrayType extends ReferenceType { * obj , tArray(x) -> * iff tArray extends and implements obj */ - if (bottom.getTypeCode() == TC_ARRAY) + if (bottom instanceof ArrayType) return tArray(elementType.intersection (((ArrayType)bottom).elementType)); - - if (bottom.getTypeCode() == TC_CLASS) { - ClassInterfacesType bottomCIT = (ClassInterfacesType)bottom; -// int len = arrayIfacesStrs.length; -// ClassInfo[] arrayIfaces = new ClassInfo[len]; -// for (int i=0; i< arrayIfacesStrs.length; i++) -// arrayIfaces[i] -// = bottomCIT.classPath.getClassInfo(arrayIfacesStrs[i]); - if (bottomCIT.clazz == null - && implementsAllIfaces(null, arrayIfaces, bottomCIT.ifaces)) - return tRange(bottomCIT, this); - } - return tError; + + return super.createRangeType(bottom); } /** @@ -100,9 +113,8 @@ public class ArrayType extends ReferenceType { */ public Type getSpecializedType(Type type) { /* - * tArray(x), object -> tArray(x) iff tArray implements object * tArray(x), tArray(y) -> tArray(x.intersection(y)) - * tArray(x), other -> tError + * tArray(x), other -> tArray(x) iff tArray implements object */ if (type.getTypeCode() == TC_RANGE) { type = ((RangeType) type).getBottom(); @@ -113,12 +125,8 @@ public class ArrayType extends ReferenceType { return tArray(elementType.intersection (((ArrayType)type).elementType)); } - if (type.getTypeCode() == TC_CLASS) { - ClassInterfacesType other = (ClassInterfacesType) type; - if (other.clazz == null - && implementsAllIfaces(null, arrayIfaces, other.ifaces)) - return this; - } + if (type.isSubTypeOf(this)) + return this; return tError; } @@ -138,78 +146,14 @@ public class ArrayType extends ReferenceType { } if (type == tNull) return this; - if (type.getTypeCode() == TC_ARRAY) + if (type instanceof ArrayType) return tArray(elementType.intersection (((ArrayType)type).elementType)); - if (type.getTypeCode() == TC_CLASS) { - ClassInterfacesType other = (ClassInterfacesType) type; - if (implementsAllIfaces(other.clazz, other.ifaces, arrayIfaces)) - return ClassInterfacesType.create(null, arrayIfaces); - if (other.clazz == null - && implementsAllIfaces(null, arrayIfaces, other.ifaces)) - return other; - - /* Now the more complicated part: find all interfaces, that are - * implemented by one interface or class in each group. - * - * First get all interfaces of this.clazz and this.ifaces. - */ - Vector newIfaces = new Vector(); - try { - iface_loop: - for (int i=0; i < arrayIfaces.length; i++) { - /* Now consider each array interface. If any clazz or - * interface in other implements it, add it to the - * newIfaces vector. */ - if (other.clazz != null - && arrayIfaces[i].implementedBy(other.clazz)) { - newIfaces.addElement(arrayIfaces[i]); - continue iface_loop; - } - for (int j=0; j + * + * @author Jochen Hoenicke */ +public class ClassInfoType extends ClassType { + ClassInfo clazz; + ClassType superClass = null; + ClassType[] interfaces = null; + + public ClassInfo getClazz() { + return clazz; + } + + public ClassInfoType(ClassInfo clazz) { + super(TC_CLASS, clazz.getName()); + this.clazz = clazz; + try { + clazz.load(ClassInfo.HIERARCHY); + } catch (IOException ex) { + clazz.guess(ClassInfo.HIERARCHY); + GlobalOptions.err.println + ("Can't get full class hierarchy for "+clazz+ + " types may be incorrect."); + GlobalOptions.err.println(ex.toString()); + } + } + + public boolean isUnknown() { + return clazz.isGuessed(); + } + + public boolean isFinal() { + return Modifier.isFinal(clazz.getModifiers()); + } + + public boolean isInterface() { + return clazz.isInterface(); + } + + public ClassType getSuperClass() { + if (clazz.isInterface()) + return null; + if (superClass == null) { + ClassInfo superInfo = clazz.getSuperclass(); + if (superInfo == null) + return null; + superClass = Type.tClass(superInfo); + } + return superClass; + } + + public ClassType[] getInterfaces() { + if (interfaces == null) { + ClassInfo[] ifaceInfos = clazz.getInterfaces(); + if (ifaceInfos.length == 0) + interfaces = EMPTY_IFACES; + else { + interfaces = new ClassType[ifaceInfos.length]; + for (int i=0; i < interfaces.length; i++) + interfaces[i] = Type.tClass(ifaceInfos[i]); + } + } + return interfaces; + } + + public ClassInfo getClassInfo() { + return clazz; + } + + public boolean equals(Object o) { + if (o instanceof ClassInfoType) + return ((ClassInfoType) o).clazz == clazz; + return super.equals(o); + } +} + + + + diff --git a/jode/jode/type/ClassInterfacesType.java b/jode/jode/type/ClassInterfacesType.java deleted file mode 100644 index 6affbb6..0000000 --- a/jode/jode/type/ClassInterfacesType.java +++ /dev/null @@ -1,624 +0,0 @@ -/* ClassInterfacesType 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 a type aproximation, consisting of multiple - * interfaces and a class type.

- * - * 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 0) - return "L" + ifaces[0].getName().replace('.','/') + ";"; - else - return "Ljava/lang/Object;"; - } - - public Class getTypeClass() throws ClassNotFoundException { - if (clazz != null) - return Class.forName(clazz.getName()); - else if (ifaces.length > 0) - return Class.forName(ifaces[0].getName()); - else - return Class.forName("java.lang.Object"); - } - - public ClassInfo getClassInfo() { - if (clazz != null) - return clazz; - else if (ifaces.length > 0) - return ifaces[0]; - else - return ClassInfo.forName("java.lang.Object"); - } - - public String toString() - { - if (this == tObject) - return "java.lang.Object"; - - if (ifaces.length == 0) - return clazz.getName(); - if (clazz == null && ifaces.length == 1) - return ifaces[0].getName(); - - StringBuffer sb = new StringBuffer("{"); - String comma = ""; - if (clazz != null) { - sb = sb.append(clazz.getName()); - comma = ", "; - } - for (int i=0; i< ifaces.length; i++) { - sb.append(comma).append(ifaces[i].getName()); - comma = ", "; - } - return sb.append("}").toString(); - } - - /** - * 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) { - Type hintType = fromType.getHint(); - switch (hintType.getTypeCode()) { - case TC_ARRAY: - if (clazz == null - && implementsAllIfaces(null, ArrayType.arrayIfaces, - this.ifaces)) - return null; - else - return tObject; - case TC_CLASS: - 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; - } - case TC_UNKNOWN: - return null; - } - return tObject; - } - - /** - * Checks if this type represents a valid type instead of a list - * of minimum types. - */ - public boolean isValidType() { - return ifaces.length == 0 - || (clazz == null && ifaces.length == 1); - } - - /** - * 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; - } - - private final static Hashtable keywords = new Hashtable(); - static { - keywords.put("if", Boolean.TRUE); - keywords.put("else", Boolean.TRUE); - keywords.put("for", Boolean.TRUE); - keywords.put("while", Boolean.TRUE); - keywords.put("throw", Boolean.TRUE); - keywords.put("return", Boolean.TRUE); - keywords.put("class", Boolean.TRUE); - keywords.put("interface", Boolean.TRUE); - keywords.put("implements", Boolean.TRUE); - keywords.put("extends", Boolean.TRUE); - keywords.put("instanceof", Boolean.TRUE); - keywords.put("new", Boolean.TRUE); - keywords.put("int", Boolean.TRUE); - keywords.put("boolean", Boolean.TRUE); - keywords.put("long", Boolean.TRUE); - keywords.put("float", Boolean.TRUE); - keywords.put("double", Boolean.TRUE); - keywords.put("short", Boolean.TRUE); - keywords.put("public", Boolean.TRUE); - keywords.put("protected", Boolean.TRUE); - keywords.put("private", Boolean.TRUE); - keywords.put("static", Boolean.TRUE); - keywords.put("synchronized", Boolean.TRUE); - keywords.put("strict", Boolean.TRUE); - keywords.put("transient", Boolean.TRUE); - keywords.put("abstract", Boolean.TRUE); - keywords.put("volatile", Boolean.TRUE); - keywords.put("final", Boolean.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() { - ClassInfo type; - if (clazz != null) - type = clazz; - else if (ifaces.length > 0) - type = ifaces[0]; - else - type = ClassInfo.forName("java.lang.Object"); - String name = type.getName(); - int dot = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$')); - if (dot >= 0) - name = name.substring(dot+1); - if (Character.isUpperCase(name.charAt(0))) { - name = name.toLowerCase(); - if (keywords.get(name) != null) - return "var_" + name; - return name; - } else - return "var_" + name; - } - - public int hashCode() { - int hash = clazz == null ? 0 : clazz.hashCode(); - for (int i=0; i < ifaces.length; i++) { - hash ^= ifaces[i].hashCode(); - } - return hash; - } - - public boolean equals(Object o) { - if (o == this) - return true; - if (o instanceof Type && ((Type)o).typecode == TC_CLASS) { - ClassInterfacesType type = (ClassInterfacesType) o; - if (type.clazz == clazz - && type.ifaces.length == ifaces.length) { - big_loop: - for (int i=0; i< type.ifaces.length; i++) { - for (int j=0; j + * + * @author Jochen Hoenicke + */ +public abstract class ClassType extends ReferenceType { + protected String className; + + public String getClassName() { + return className; + } + + public ClassType(int typecode, String clazzName) { + super(typecode); + className = clazzName; + } + + /** + * Checks if this type represents an interface. + * @return true if this is an interface, false if this is a class or + * if unsure. + */ + public abstract boolean isUnknown(); + + /** + * Checks if this type represents an interface. + * @return true if this is an interface, false if this is a class or + * if unsure. + */ + public abstract boolean isInterface(); + + /** + * Checks if this type represents an interface. + * @return true if this is an interface, false if this is a class or + * if unsure. + */ + public abstract boolean isFinal(); + + /** + * Returns the reference type representing the super class, or null + * if this reference type represents java.lang.Object, or for interfaces. + * @return the super class' type. + */ + public abstract ClassType getSuperClass(); + + /** + * Returns the reference type representing the interfaces this class + * implements. This may of course be an empty array. + * @return the interfaces' types. + */ + public abstract ClassType[] getInterfaces(); + + public boolean isSubTypeOf(Type type) { + if (type == tNull) + return true; + if (type instanceof MultiClassType) + return ((MultiClassType)type).containsSuperTypeOf(this); + if (!(type instanceof ClassType)) + return false; + if (this.equals(tObject)) + return true; + + ClassType ctype = (ClassType) type; + + if (isFinal()) + return ctype.equals(this); + + while (ctype != null) { + if (ctype.equals(this)) + return true; + + if (isInterface()) { + ClassType[] typeIfaces = ctype.getInterfaces(); + for (int i = 0; i < typeIfaces.length; i++) + if (isSubTypeOf(typeIfaces[i])) + return true; + } + ctype = ctype.getSuperClass(); + } + return false; + } + + public boolean maybeSubTypeOf(Type type) { + if (type == tNull) + return true; + if (!(type instanceof ClassType)) + return false; + if (this.equals(tObject)) + return true; + + ClassType ctype = (ClassType) type; + + if (isFinal()) + return ctype.equals(this); + + while (ctype != null) { + if (ctype.equals(this)) + return true; + + if (ctype.isUnknown()) + return true; + + if (isInterface()) { + ClassType[] typeIfaces = ctype.getInterfaces(); + for (int i = 0; i < typeIfaces.length; i++) + if (isSubTypeOf(typeIfaces[i])) + return true; + } + ctype = ctype.getSuperClass(); + } + return false; + } + + public Type getSubType() { + return tRange(this, tNull); + } + + public Type getHint() { + return this; + } + + public Type getCanonic() { + return this; + } + + /** + * 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.maybeSubTypeOf(this)) + return tError; + + if (this.isSubTypeOf(bottomType)) + /* bottomType contains a class equal to this. + */ + return this; + return tRange(bottomType, this); + } + + /** + * 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) { + if (type instanceof RangeType) { + type = ((RangeType) type).getBottom(); + } + + /* Most times (almost always) one of the two classes is + * already more specialized. Optimize for this case. + */ + if (type.isSubTypeOf(this)) + return this; + if (this.isSubTypeOf(type)) + return type; + + if (type instanceof MultiClassType) + return ((MultiClassType) type).getSpecializedType(this); + + if (!(type instanceof ClassType)) + return tError; + ClassType other = (ClassType) type; + return MultiClassType.create(new ClassType[] {this, other}); + } + + /** + * 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) { + int code = type.typecode; + if (code == TC_RANGE) { + type = ((RangeType) type).getTop(); + code = type.typecode; + } + if (code == TC_NULL) + return this; + + /* 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(); + classTypes.push(this); + return ((ReferenceType) type).findCommonClassTypes(classTypes); + } + + public String getTypeSignature() { + return "L" + className + ";"; + } + + public Class getTypeClass() throws ClassNotFoundException { + return Class.forName(className); + } + + public String toString() + { + return className; + } + + /** + * 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) { + if (fromType.getHint().isSubTypeOf(this)) + return null; + return tObject; + } + + /** + * Checks if this type represents a valid type instead of a list + * of minimum types. + */ + public boolean isValidType() { + return true; + } + + /** + * 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; + } + + private final static Hashtable keywords = new Hashtable(); + static { + keywords.put("if", Boolean.TRUE); + keywords.put("else", Boolean.TRUE); + keywords.put("for", Boolean.TRUE); + keywords.put("while", Boolean.TRUE); + keywords.put("throw", Boolean.TRUE); + keywords.put("return", Boolean.TRUE); + keywords.put("class", Boolean.TRUE); + keywords.put("interface", Boolean.TRUE); + keywords.put("implements", Boolean.TRUE); + keywords.put("extends", Boolean.TRUE); + keywords.put("instanceof", Boolean.TRUE); + keywords.put("new", Boolean.TRUE); + keywords.put("int", Boolean.TRUE); + keywords.put("boolean", Boolean.TRUE); + keywords.put("long", Boolean.TRUE); + keywords.put("float", Boolean.TRUE); + keywords.put("double", Boolean.TRUE); + keywords.put("short", Boolean.TRUE); + keywords.put("public", Boolean.TRUE); + keywords.put("protected", Boolean.TRUE); + keywords.put("private", Boolean.TRUE); + keywords.put("static", Boolean.TRUE); + keywords.put("synchronized", Boolean.TRUE); + keywords.put("strict", Boolean.TRUE); + keywords.put("transient", Boolean.TRUE); + keywords.put("abstract", Boolean.TRUE); + keywords.put("volatile", Boolean.TRUE); + keywords.put("final", Boolean.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() { + String name = className; + int dot = Math.max(name.lastIndexOf('.'), name.lastIndexOf('$')); + if (dot >= 0) + name = name.substring(dot+1); + if (Character.isUpperCase(name.charAt(0))) { + name = name.toLowerCase(); + if (keywords.get(name) != null) + return "var_" + name; + return name; + } else + return "var_" + name; + } + + public int hashCode() { + return className.hashCode(); + } + + public boolean equals(Object o) { + if (o instanceof ClassType) + return ((ClassType) o).className.equals(className); + return false; + } +} diff --git a/jode/jode/type/IntegerType.java b/jode/jode/type/IntegerType.java index acc8041..1087a0b 100644 --- a/jode/jode/type/IntegerType.java +++ b/jode/jode/type/IntegerType.java @@ -138,6 +138,16 @@ public class IntegerType extends Type { return true; } + /** + * Returns true, if this is a sub type of type. + */ + public boolean isSubtypeOf(Type type) { + if (type.typecode != TC_INTEGER) + return false; + int otherTypes = ((IntegerType)type).possTypes; + return (otherTypes & possTypes) == otherTypes; + } + /** * Check if this and <unknown -- type&rt; are not disjunct. * @param type a simple type; this mustn't be a range type. diff --git a/jode/jode/type/Makefile.am b/jode/jode/type/Makefile.am index 57761bd..d08c985 100644 --- a/jode/jode/type/Makefile.am +++ b/jode/jode/type/Makefile.am @@ -10,12 +10,15 @@ FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH): MY_JAVA_FILES = \ ArrayType.java \ - ClassInterfacesType.java \ + ClassType.java \ + ClassInfoType.java \ IntegerType.java \ MethodType.java \ + MultiClassType.java \ NullType.java \ RangeType.java \ ReferenceType.java \ + SystemClassType.java \ Type.java noinst_DATA = $(MY_JAVA_FILES:.java=.class) diff --git a/jode/jode/type/MethodType.java b/jode/jode/type/MethodType.java index 5aa491e..4fa8e55 100644 --- a/jode/jode/type/MethodType.java +++ b/jode/jode/type/MethodType.java @@ -18,6 +18,7 @@ */ package jode.type; +import jode.bytecode.ClassPath; /** * This type represents an method type. @@ -28,9 +29,11 @@ public class MethodType extends Type { final String signature; final Type[] parameterTypes; final Type returnType; + final ClassPath cp; - public MethodType(String signature) { + public MethodType(ClassPath cp, String signature) { super(TC_METHOD); + this.cp = cp; this.signature = signature; int index = 1, types = 0; while (signature.charAt(index) != ')') { @@ -53,9 +56,9 @@ public class MethodType extends Type { index = signature.indexOf(';', index); index++; parameterTypes[types++] - = Type.tType(signature.substring(lastindex,index)); + = Type.tType(cp, signature.substring(lastindex,index)); } - returnType = Type.tType(signature.substring(index+1)); + returnType = Type.tType(cp, signature.substring(index+1)); } public final int stackSize() { @@ -65,6 +68,10 @@ public class MethodType extends Type { return size; } + public ClassPath getClassPath() { + return cp; + } + public Type[] getParameterTypes() { return parameterTypes; } @@ -91,10 +98,4 @@ public class MethodType extends Type { public String toString() { return signature; } - - public boolean equals(Object o) { - MethodType mt; - return (o instanceof MethodType - && signature.equals((mt = (MethodType)o).signature)); - } } diff --git a/jode/jode/type/MultiClassType.java b/jode/jode/type/MultiClassType.java new file mode 100644 index 0000000..6f029b6 --- /dev/null +++ b/jode/jode/type/MultiClassType.java @@ -0,0 +1,315 @@ +/* MultiClassType 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.Stack; +import java.util.Vector; +import java.io.IOException; + +/** + * This class represents a type aproximation, consisting of multiple + * interfaces and a class type.

+ * + * 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 - *

ClassInterfacesType
- *
All types in this range must be widening castable to all interfaces - * and to the class in the bottomType
+ *
MultiClassType
+ *
All types in this range must be widening castable to all classes + * in the bottomType
*
ArrayType
*
All types in this range must be of the bottomType, or the * NullType.
@@ -213,8 +213,9 @@ public class RangeType extends Type { result = tError; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) { - GlobalOptions.err.println("intersecting "+ this +" and "+ type + - " to " + result); + GlobalOptions.err.println("intersecting "+ this +" and "+ type + + " to <" + bottom + "," + top + + "> to " + result); } return result; } diff --git a/jode/jode/type/ReferenceType.java b/jode/jode/type/ReferenceType.java index 5ef55a4..cbcc796 100644 --- a/jode/jode/type/ReferenceType.java +++ b/jode/jode/type/ReferenceType.java @@ -21,10 +21,14 @@ package jode.type; import jode.GlobalOptions; import jode.bytecode.ClassInfo; import java.io.IOException; +import java.util.Stack; +import java.util.Vector; +import java.util.Enumeration; /** * This is an abstrace super class of all reference types. Reference - * types are ClassInterfacesType, ArrayType and NullType.

+ * 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.