Big updates: bytecode instruction interface, new types for the decompiler,

option parsing a bit reworked.


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1267 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 24 years ago
parent 5e6af53990
commit 4a63627c87
  1. 9
      jode/jode/GlobalOptions.java
  2. 52
      jode/jode/decompiler/ClassAnalyzer.java
  3. 48
      jode/jode/decompiler/Decompiler.java
  4. 2
      jode/jode/decompiler/FieldAnalyzer.java
  5. 42
      jode/jode/decompiler/ImportHandler.java
  6. 7
      jode/jode/decompiler/LocalInfo.java
  7. 55
      jode/jode/decompiler/Main.java
  8. 68
      jode/jode/decompiler/MethodAnalyzer.java
  9. 9
      jode/jode/decompiler/Opcodes.java
  10. 7
      jode/jode/decompiler/Options.java
  11. 40
      jode/jode/decompiler/OuterValues.java
  12. 153
      jode/jode/decompiler/TabbedPrintWriter.java
  13. 1
      jode/jode/expr/Expression.java
  14. 28
      jode/jode/expr/FieldOperator.java
  15. 8
      jode/jode/expr/IfThenElseOperator.java
  16. 72
      jode/jode/expr/InvokeOperator.java
  17. 1
      jode/jode/expr/NewArrayOperator.java
  18. 6
      jode/jode/flow/CreateClassField.java
  19. 20
      jode/jode/flow/FlowBlock.java
  20. 179
      jode/jode/flow/TransformConstructors.java
  21. 9
      jode/jode/flow/TryBlock.java
  22. 3
      jode/jode/jvm/CodeVerifier.java
  23. 2
      jode/jode/jvm/Interpreter.java
  24. 26
      jode/jode/jvm/SyntheticAnalyzer.java
  25. 23
      jode/jode/obfuscator/ClassBundle.java
  26. 3
      jode/jode/obfuscator/ClassIdentifier.java
  27. 4
      jode/jode/obfuscator/CodeAnalyzer.java
  28. 4
      jode/jode/obfuscator/CodeTransformer.java
  29. 8
      jode/jode/obfuscator/ConstantRuntimeEnvironment.java
  30. 5
      jode/jode/obfuscator/FieldIdentifier.java
  31. 116
      jode/jode/obfuscator/MethodIdentifier.java
  32. 1639
      jode/jode/obfuscator/modules/ConstantAnalyzer.java
  33. 833
      jode/jode/obfuscator/modules/LocalOptimizer.java
  34. 2
      jode/jode/obfuscator/modules/Makefile.am
  35. 760
      jode/jode/obfuscator/modules/RemovePopAnalyzer.java
  36. 168
      jode/jode/obfuscator/modules/SimpleAnalyzer.java
  37. 152
      jode/jode/type/ArrayType.java
  38. 108
      jode/jode/type/ClassInfoType.java
  39. 624
      jode/jode/type/ClassInterfacesType.java
  40. 332
      jode/jode/type/ClassType.java
  41. 10
      jode/jode/type/IntegerType.java
  42. 5
      jode/jode/type/Makefile.am
  43. 19
      jode/jode/type/MethodType.java
  44. 315
      jode/jode/type/MultiClassType.java
  45. 16
      jode/jode/type/NullType.java
  46. 9
      jode/jode/type/RangeType.java
  47. 50
      jode/jode/type/ReferenceType.java
  48. 72
      jode/jode/type/SystemClassType.java
  49. 110
      jode/jode/type/Type.java

@ -80,12 +80,13 @@ public class GlobalOptions {
/** /**
* Parse the argument given to the debugging flag. * 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) { public static boolean setDebugging(String debuggingString) {
if (debuggingString.length() == 0 || debuggingString.equals("help")) { if (debuggingString.length() == 0 || debuggingString.equals("help")) {
usageDebugging(); usageDebugging();
return false; throw new IllegalArgumentException();
} }
StringTokenizer st = new StringTokenizer(debuggingString, ","); StringTokenizer st = new StringTokenizer(debuggingString, ",");
@ -98,8 +99,8 @@ public class GlobalOptions {
continue next_token; continue next_token;
} }
} }
err.println("Illegal debugging flag: "+token); throw new IllegalArgumentException("Illegal debugging flag: "
return false; +token);
} }
return true; return true;
} }

@ -23,6 +23,7 @@ import jode.type.MethodType;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassFormatException; import jode.bytecode.ClassFormatException;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath;
import jode.bytecode.FieldInfo; import jode.bytecode.FieldInfo;
import jode.bytecode.MethodInfo; import jode.bytecode.MethodInfo;
import jode.expr.Expression; import jode.expr.Expression;
@ -72,7 +73,14 @@ public class ClassAnalyzer
MethodAnalyzer staticConstructor; MethodAnalyzer staticConstructor;
MethodAnalyzer[] constructors; MethodAnalyzer[] constructors;
/**
* The outer values for method scoped classes.
*/
OuterValues outerValues; OuterValues outerValues;
/**
* The outer instance for non-static class scope classes.
*/
Expression outerInstance;
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
ClassInfo clazz, ImportHandler imports, ClassInfo clazz, ImportHandler imports,
@ -97,7 +105,7 @@ public class ClassAnalyzer
? "public" : "all") ? "public" : "all")
+ " information of " + superClass + " information of " + superClass
+" to detect name conflicts."); +" to detect name conflicts.");
ex.printStackTrace(GlobalOptions.err); GlobalOptions.err.println(ex.toString());
superClass.guess(howMuch); superClass.guess(howMuch);
} }
superClass = superClass.getSuperclass(); superClass = superClass.getSuperclass();
@ -106,26 +114,18 @@ public class ClassAnalyzer
this.parent = parent; this.parent = parent;
this.clazz = clazz; this.clazz = clazz;
this.imports = imports; this.imports = imports;
if (outerValues != null)
this.outerValues = new OuterValues(this, outerValues);
modifiers = clazz.getModifiers(); modifiers = clazz.getModifiers();
name = clazz.getClassName(); name = clazz.getClassName();
if (parent != null) { /* Check if this is a normal non-static inner class and set
ClassInfo outerClazz = clazz.getOuterClass(); * outerInstance.
if (outerClazz == null) { */
if (parent instanceof ClassAnalyzer) if ((Options.options & Options.OPTION_INNER) != 0
throw new jode.AssertError && parent instanceof ClassAnalyzer && !isStatic())
("ClassInfo Attributes are inconsistent: " outerInstance = new ThisOperator(((ClassAnalyzer) parent).clazz);
+ clazz.getName()+" parent: "+parent); if (outerValues != null)
} else { this.outerValues = new OuterValues(this, outerValues);
if (!(parent instanceof ClassAnalyzer)
|| ((ClassAnalyzer) parent).clazz != outerClazz)
throw new jode.AssertError
("ClassInfo Attributes are inconsistent: "
+ clazz.getName()+" parent: "+parent);
}
}
} }
public ClassAnalyzer(ClassDeclarer parent, public ClassAnalyzer(ClassDeclarer parent,
@ -141,6 +141,10 @@ public class ClassAnalyzer
this(null, clazz, imports); this(null, clazz, imports);
} }
public ClassPath getClassPath() {
return clazz.getClassPath();
}
public final boolean isStatic() { public final boolean isStatic() {
return Modifier.isStatic(modifiers); return Modifier.isStatic(modifiers);
} }
@ -196,6 +200,10 @@ public class ClassAnalyzer
return outerValues; return outerValues;
} }
public Expression getOuterInstance() {
return outerInstance;
}
public void addBlockInitializer(int index, StructuredBlock initializer) { public void addBlockInitializer(int index, StructuredBlock initializer) {
if (blockInitializers[index] == null) if (blockInitializers[index] == null)
blockInitializers[index] = initializer; blockInitializers[index] = initializer;
@ -218,18 +226,12 @@ public class ClassAnalyzer
if ((Options.options & Options.OPTION_INNER) != 0 if ((Options.options & Options.OPTION_INNER) != 0
&& innerInfos != null) { && innerInfos != null) {
/* Create inner classes */ /* Create inner classes */
Expression[] outerThis = new Expression[] {
new ThisOperator(clazz)
};
int innerCount = innerInfos.length; int innerCount = innerInfos.length;
inners = new ClassAnalyzer[innerCount]; inners = new ClassAnalyzer[innerCount];
for (int i=0; i < innerCount; i++) { for (int i=0; i < innerCount; i++) {
try { try {
inners[i] = new ClassAnalyzer inners[i] = new ClassAnalyzer
(this, innerInfos[i], imports, (this, innerInfos[i], imports, null);
Modifier.isStatic(innerInfos[i].getModifiers())
? null : outerThis);
} catch (ClassFormatException ex) { } catch (ClassFormatException ex) {
GlobalOptions.err.println("Inner class "+innerInfos[i] GlobalOptions.err.println("Inner class "+innerInfos[i]
+" malformed!"); +" malformed!");

@ -42,6 +42,11 @@ public class Decompiler {
private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT; private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT; private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;
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 * We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs. * UNIX System) is used a protocol separator in URLs.
@ -54,7 +59,9 @@ public class Decompiler {
= ClassPath.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() { public Decompiler() {
} }
@ -108,14 +115,28 @@ public class Decompiler {
*/ */
public void setOption(String option, String value) { public void setOption(String option, String value) {
if (option.equals("style")) { if (option.equals("style")) {
if (value.equals("gnu")) if (value.equals("gnu")) {
Options.outputStyle = Options.GNU_STYLE; outputStyle = 0;
else if (value.equals("sun")) indentSize = 2;
Options.outputStyle = Options.SUN_STYLE; } else if (value.equals("sun")) {
else outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
indentSize = 4;
} else
throw new IllegalArgumentException("Invalid style "+value); throw new IllegalArgumentException("Invalid style "+value);
return; 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")) { if (option.equals("import")) {
int comma = value.indexOf(','); int comma = value.indexOf(',');
int packLimit = Integer.parseInt(value.substring(0, comma)); int packLimit = Integer.parseInt(value.substring(0, comma));
@ -135,6 +156,10 @@ public class Decompiler {
GlobalOptions.verboseLevel = Integer.parseInt(value); GlobalOptions.verboseLevel = Integer.parseInt(value);
return; return;
} }
if (option.equals("debug")) {
GlobalOptions.setDebugging(value);
return;
}
for (int i=0; i < optionStrings.length; i++) { for (int i=0; i < optionStrings.length; i++) {
if (option.equals(optionStrings[i])) { if (option.equals(optionStrings[i])) {
if (value.equals("0") if (value.equals("0")
@ -184,8 +209,11 @@ public class Decompiler {
ProgressListener progress) ProgressListener progress)
throws java.io.IOException { throws java.io.IOException {
if (classPath == null) { if (classPath == null) {
String cp = System.getProperty("java.class.path") String cp = System.getProperty("java.class.path");
.replace(File.pathSeparatorChar, altPathSeparatorChar); 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); classPath = new ClassPath(cp);
} }
@ -196,7 +224,9 @@ public class Decompiler {
ImportHandler imports = new ImportHandler(importPackageLimit, ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit); importClassLimit);
TabbedPrintWriter tabbedWriter = TabbedPrintWriter tabbedWriter =
new TabbedPrintWriter(writer, imports, false); new TabbedPrintWriter(writer, imports, false,
outputStyle, indentSize,
tabWidth, lineWidth);
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports); ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
clazzAna.dumpJavaFile(tabbedWriter, progress); clazzAna.dumpJavaFile(tabbedWriter, progress);
writer.flush(); writer.flush();

@ -51,7 +51,7 @@ public class FieldAnalyzer implements Analyzer {
imports = i; imports = i;
modifiers = fd.getModifiers(); modifiers = fd.getModifiers();
type = Type.tType(fd.getType()); type = Type.tType(cla.getClassPath(), fd.getType());
fieldName = fd.getName(); fieldName = fd.getName();
constant = null; constant = null;
this.isSynthetic = fd.isSynthetic(); this.isSynthetic = fd.isSynthetic();

@ -22,7 +22,8 @@ import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.type.Type; import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.type.ClassInterfacesType; import jode.type.ClassInfoType;
import jode.type.ClassType;
import jode.type.NullType; import jode.type.NullType;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
@ -274,7 +275,13 @@ public class ImportHandler {
clazz = outer; 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); Integer i = (Integer) imports.get(name);
if (i == null) { if (i == null) {
@ -308,8 +315,10 @@ public class ImportHandler {
public final void useType(Type type) { public final void useType(Type type) {
if (type instanceof ArrayType) if (type instanceof ArrayType)
useType(((ArrayType) type).getElementType()); useType(((ArrayType) type).getElementType());
else if (type instanceof ClassInterfacesType) else if (type instanceof ClassInfoType)
useClass(((ClassInterfacesType) type).getClassInfo()); 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. * @return a legal string representation of clazz.
*/ */
public String getClassString(ClassInfo 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.
* <p>
* Known Bug 1: If this is called before the imports are cleaned up,
* (that is only for debugging messages), the result is unpredictable.
* <p>
* 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) if (cachedClassNames == null)
/* We are not yet clean, return the full name */ /* We are not yet clean, return the full name */
return name; return name;
@ -358,8 +384,10 @@ public class ImportHandler {
public String getTypeString(Type type) { public String getTypeString(Type type) {
if (type instanceof ArrayType) if (type instanceof ArrayType)
return getTypeString(((ArrayType) type).getElementType()) + "[]"; return getTypeString(((ArrayType) type).getElementType()) + "[]";
else if (type instanceof ClassInterfacesType) else if (type instanceof ClassInfoType)
return getClassString(((ClassInterfacesType) type).getClassInfo()); return getClassString(((ClassInfoType) type).getClassInfo());
else if (type instanceof ClassType)
return getClassString(((ClassType) type).getClassName());
else if (type instanceof NullType) else if (type instanceof NullType)
return "Object"; return "Object";
else else

@ -248,7 +248,8 @@ public class LocalInfo implements Declarable {
return shadow.getName(); return shadow.getName();
} }
if (name == null) { if (name == null) {
return "local_" + slot + "_" + Integer.toHexString(hashCode()); return "local_" + (slot >= 0 ? slot + "_" : "")
+ Integer.toHexString(hashCode());
} }
return name; return name;
} }
@ -312,7 +313,9 @@ public class LocalInfo implements Declarable {
&& otherType != Type.tError && li.type != Type.tError) { && otherType != Type.tError && li.type != Type.tError) {
GlobalOptions.err.println("Type error in local " + getName()+": " GlobalOptions.err.println("Type error in local " + getName()+": "
+ li.type + " and " + otherType); + li.type + " and " + otherType);
Thread.dumpStack(); if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0)
Thread.dumpStack();
} }
else if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) else if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println(getName()+" setType, new: "+newType GlobalOptions.err.println(getName()+" setType, new: "+newType

@ -25,6 +25,7 @@ import jode.GlobalOptions;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@ -47,7 +48,6 @@ public class Main extends Options {
new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'),
new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'),
new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'), new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'),
new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'),
new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null,
OPTION_START+0), OPTION_START+0),
new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null, new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null,
@ -86,40 +86,6 @@ public class Main extends Options {
"The directories should be separated by ','."); "The directories should be separated by ','.");
err.println(" -d, --dest <dir> "+ err.println(" -d, --dest <dir> "+
"write decompiled files to disk into directory destdir."); "write decompiled files to disk into directory destdir.");
err.println(" -s, --style {sun|gnu} "+
"specify indentation style");
err.println(" -i, --import <pkglimit>,<clslimit>");
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) { public static boolean handleOption(int option, int longind, String arg) {
@ -197,19 +163,6 @@ public class Main extends Options {
errorInParams |= !GlobalOptions.setDebugging(arg); errorInParams |= !GlobalOptions.setDebugging(arg);
break; 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': { case 'i': {
String arg = g.getOptarg(); String arg = g.getOptarg();
int comma = arg.indexOf(','); int comma = arg.indexOf(',');
@ -311,6 +264,12 @@ public class Main extends Options {
writer.close(); writer.close();
/* Now is a good time to clean up */ /* Now is a good time to clean up */
System.gc(); 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) { } catch (IOException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("Can't write source of "+params[i]+"."); ("Can't write source of "+params[i]+".");

@ -195,7 +195,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
this.imports = imports; this.imports = imports;
this.minfo = minfo; this.minfo = minfo;
this.methodName = minfo.getName(); this.methodName = minfo.getName();
this.methodType = Type.tMethod(minfo.getType()); this.methodType = Type.tMethod(cla.getClassPath(), minfo.getType());
this.isConstructor = this.isConstructor =
methodName.equals("<init>") || methodName.equals("<clinit>"); methodName.equals("<init>") || methodName.equals("<clinit>");
@ -209,7 +209,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int excCount = excattr.length; int excCount = excattr.length;
this.exceptions = new Type[excCount]; this.exceptions = new Type[excCount];
for (int i=0; i< excCount; i++) 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) if (minfo.isSynthetic() || methodName.indexOf('$') != -1)
synth = new SyntheticAnalyzer(cla.getClazz(), minfo, true); 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 * Create a local info for a local variable located at an
* instruction with the given address. * instruction with the given address.
* @param addr the address of the instruction using this local. * @param lvi the local variable info of the bytecode package.
* the address of the next instruction for stores.
* @param slot the slot, the local variable uses.
* @return a new local info representing that local. * @return a new local info representing that local.
*/ */
public LocalInfo getLocalInfo(LocalVariableInfo lvi) { public LocalInfo getLocalInfo(LocalVariableInfo lvi) {
LocalInfo li = new LocalInfo(this, lvi.getSlot()); LocalInfo li = new LocalInfo(this, lvi.getSlot());
if (lvi.getName() != null) if ((Options.options & Options.OPTION_LVT) != 0
li.addHint(lvi.getName(), Type.tType(lvi.getType())); && lvi.getName() != null)
li.addHint(lvi.getName(), Type.tType(classAnalyzer.getClassPath(),
lvi.getType()));
allLocals.addElement(li); allLocals.addElement(li);
return li; return li;
} }
@ -427,7 +428,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int count = 0; int count = 0;
Block[] blocks = bb.getBlocks(); Block[] blocks = bb.getBlocks();
for (int i=0; i < blocks.length; i++) for (int i=0; i < blocks.length; i++)
count += blocks[i].getInstructions().size(); count += blocks[i].getInstructions().length;
return (double) count; return (double) count;
} }
} }
@ -462,10 +463,9 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int count = 0; int count = 0;
for (int i=0; i < blocks.length; i++) { for (int i=0; i < blocks.length; i++) {
int mark = 100; int mark = 100;
int last = blocks[i].getInstructions().size() - 1; int last = blocks[i].getInstructions().length - 1;
for (int j=0; j <= last; j++) { for (int j=0; j <= last; j++) {
Instruction instr Instruction instr = blocks[i].getInstructions()[j];
= (Instruction) blocks[i].getInstructions().get(j);
if (GlobalOptions.verboseLevel > 0 && j > mark) { if (GlobalOptions.verboseLevel > 0 && j > mark) {
GlobalOptions.err.print('.'); GlobalOptions.err.print('.');
mark += 100; mark += 100;
@ -494,6 +494,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
methodHeader = new FlowBlock(this, 0, null); methodHeader = new FlowBlock(this, 0, null);
else else
methodHeader = flows[startBlock.getBlockNr()]; methodHeader = flows[startBlock.getBlockNr()];
methodHeader.addStartPred();
Handler[] handlers = bb.getExceptionHandlers(); Handler[] handlers = bb.getExceptionHandlers();
excHandlers = new TransformExceptionHandlers(flows); excHandlers = new TransformExceptionHandlers(flows);
@ -504,7 +505,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
FlowBlock handler FlowBlock handler
= flows[handlers[i].getCatcher().getBlockNr()]; = flows[handlers[i].getCatcher().getBlockNr()];
if (handlers[i].getType() != null) 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); excHandlers.addHandler(start, end, handler, type);
} }
@ -515,6 +517,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
excHandlers.analyze(); excHandlers.analyze();
methodHeader.analyze(); methodHeader.analyze();
methodHeader.removeStartPred();
if ((Options.options & Options.OPTION_PUSH) == 0 if ((Options.options & Options.OPTION_PUSH) == 0
&& methodHeader.mapStackToLocal()) && methodHeader.mapStackToLocal())
@ -537,7 +540,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* code of this method, but not the method scoped classes. * code of this method, but not the method scoped classes.
*/ */
public void analyze(ProgressListener pl, double done, double scale) public void analyze(ProgressListener pl, double done, double scale)
throws ClassFormatError throws ClassFormatError
{ {
if (pl != null) if (pl != null)
pl.updateProgress(done, methodName); pl.updateProgress(done, methodName);
@ -546,7 +549,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
CodeVerifier verifier CodeVerifier verifier
= new CodeVerifier(getClazz(), minfo, bb); = new CodeVerifier(getClazz(), minfo, bb);
try { try {
verifier.verify(); verifier.verify();
} catch (VerifyException ex) { } catch (VerifyException ex) {
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(GlobalOptions.err);
throw new jode.AssertError("Verification error"); throw new jode.AssertError("Verification error");
@ -684,6 +687,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
boolean declareAsConstructor = isConstructor; boolean declareAsConstructor = isConstructor;
int skipParams = 0; int skipParams = 0;
int modifiedModifiers = minfo.getModifiers();
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& classAnalyzer.outerValues != null) && classAnalyzer.outerValues != null)
skipParams = classAnalyzer.outerValues.getCount(); skipParams = classAnalyzer.outerValues.getCount();
@ -693,6 +697,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
declareAsConstructor = true; declareAsConstructor = true;
skipParams = hasJikesOuterValue skipParams = hasJikesOuterValue
&& classAnalyzer.outerValues.getCount() > 0 ? 1 : 0; && classAnalyzer.outerValues.getCount() > 0 ? 1 : 0;
// get the modifiers of the real constructor
modifiedModifiers = jikesConstructor.minfo.getModifiers();
} }
if (isJikesBlockInitializer) if (isJikesBlockInitializer)
@ -710,11 +716,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* But this rule doesn't necessarily apply for anonymous * But this rule doesn't necessarily apply for anonymous
* classes... * classes...
*/ */
&& ((minfo.getModifiers() && ((modifiedModifiers
& (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE
| Modifier.SYNCHRONIZED | Modifier.STATIC | Modifier.SYNCHRONIZED | Modifier.STATIC
| Modifier.ABSTRACT | Modifier.NATIVE)) | Modifier.ABSTRACT | Modifier.NATIVE))
== (getClassAnalyzer().getModifiers() == (classAnalyzer.getModifiers()
& (Modifier.PROTECTED | Modifier.PUBLIC)) & (Modifier.PROTECTED | Modifier.PUBLIC))
|| classAnalyzer.getName() == null) || classAnalyzer.getName() == null)
&& classAnalyzer.constructors.length == 1) { && classAnalyzer.constructors.length == 1) {
@ -745,9 +751,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
int modifiedModifiers = minfo.getModifiers(); int modifiedModifiers = minfo.getModifiers();
if (isConstructor() && !isStatic() if (isConstructor() && !isStatic()
&& (Options.options & Options.OPTION_CONTRAFO) != 0 && (Options.options & Options.OPTION_CONTRAFO) != 0) {
&& classAnalyzer.outerValues != null) if (classAnalyzer.outerValues != null)
skipParams = classAnalyzer.outerValues.getCount(); skipParams = classAnalyzer.outerValues.getCount();
else if (classAnalyzer.getOuterInstance() != null)
skipParams = 1;
}
if (jikesConstructor != null) { if (jikesConstructor != null) {
// This is the real part of a jikes constructor // This is the real part of a jikes constructor
@ -790,6 +799,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (isConstructor() && isStatic()) if (isConstructor() && isStatic())
modifiedModifiers &= ~Modifier.FINAL; modifiedModifiers &= ~Modifier.FINAL;
writer.startOp(writer.NO_PAREN, 0);
writer.startOp(writer.NO_PAREN, 5);
String delim =""; String delim ="";
if (minfo.isSynthetic()) { if (minfo.isSynthetic()) {
@ -814,24 +825,35 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.printType(getReturnType()); writer.printType(getReturnType());
writer.print(" " + methodName); writer.print(" " + methodName);
} }
writer.breakOp();
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0);
int offset = skipParams + (isStatic() ? 0 : 1); int offset = skipParams + (isStatic() ? 0 : 1);
for (int i = offset; i < param.length; i++) { for (int i = offset; i < param.length; i++) {
if (i > offset) if (i > offset) {
writer.print(", "); writer.print(", ");
writer.breakOp();
}
param[i].dumpDeclaration(writer); param[i].dumpDeclaration(writer);
} }
writer.endOp();
writer.print(")"); writer.print(")");
} }
writer.endOp();
if (exceptions.length > 0) { if (exceptions.length > 0) {
writer.println(""); writer.breakOp();
writer.print("throws "); writer.print(" throws ");
writer.startOp(writer.NO_PAREN, 0);
for (int i= 0; i< exceptions.length; i++) { for (int i= 0; i< exceptions.length; i++) {
if (i > 0) if (i > 0) {
writer.print(", "); writer.print(", ");
writer.breakOp();
}
writer.printType(exceptions[i]); writer.printType(exceptions[i]);
} }
writer.endOp();
} }
writer.endOp();
if (bb != null) { if (bb != null) {
writer.openBrace(); writer.openBrace();
writer.tab(); writer.tab();

@ -140,6 +140,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
public static void addOpcode(FlowBlock flow, Instruction instr, public static void addOpcode(FlowBlock flow, Instruction instr,
MethodAnalyzer ma) MethodAnalyzer ma)
{ {
ClassPath cp = ma.getClassAnalyzer().getClassPath();
int opcode = instr.getOpcode(); int opcode = instr.getOpcode();
switch (opcode) { switch (opcode) {
case opc_nop: case opc_nop:
@ -373,7 +374,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
break; break;
} }
case opc_new: { case opc_new: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(cp, instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal(ma, instr, new NewOperator(type))); flow.appendBlock(createNormal(ma, instr, new NewOperator(type)));
break; break;
@ -388,14 +389,14 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
new ThrowBlock(new NopOperator(Type.tUObject)))); new ThrowBlock(new NopOperator(Type.tUObject))));
break; break;
case opc_checkcast: { case opc_checkcast: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(cp, instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal flow.appendBlock(createNormal
(ma, instr, new CheckCastOperator(type))); (ma, instr, new CheckCastOperator(type)));
break; break;
} }
case opc_instanceof: { case opc_instanceof: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(cp, instr.getClazzType());
ma.useType(type); ma.useType(type);
flow.appendBlock(createNormal flow.appendBlock(createNormal
(ma, instr, new InstanceOfOperator(type))); (ma, instr, new InstanceOfOperator(type)));
@ -410,7 +411,7 @@ public abstract class Opcodes implements jode.bytecode.Opcodes {
new MonitorExitOperator())); new MonitorExitOperator()));
break; break;
case opc_multianewarray: { case opc_multianewarray: {
Type type = Type.tType(instr.getClazzType()); Type type = Type.tType(cp, instr.getClazzType());
ma.useType(type); ma.useType(type);
int dimension = instr.getDimensions(); int dimension = instr.getDimensions();
flow.appendBlock(createNormal flow.appendBlock(createNormal

@ -22,11 +22,6 @@ import jode.bytecode.ClassInfo;
import java.io.IOException; import java.io.IOException;
public class Options { 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_LVT = 0x0001;
public static final int OPTION_INNER = 0x0002; public static final int OPTION_INNER = 0x0002;
public static final int OPTION_ANON = 0x0004; public static final int OPTION_ANON = 0x0004;
@ -42,8 +37,6 @@ public class Options {
OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY | OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY |
OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO; OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO;
public static int outputStyle = SUN_STYLE;
public final static boolean doAnonymous() { public final static boolean doAnonymous() {
return (options & OPTION_ANON) != 0; return (options & OPTION_ANON) != 0;
} }

@ -30,30 +30,30 @@ import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;
/** /**
* A list of local variables that a method scoped class inherits * <p>A list of local variables that a method scoped class inherits
* from its declaring method. * from its declaring method.</p>
* *
* A method scoped class is a class that is declared in a method and * <p>A method scoped class is a class that is declared in a method
* it can access other (final) local variables declared earlier. To * and it can access other (final) local variables declared earlier.
* realize this the java compiler adds hidden parameters to the * To realize this the java compiler adds hidden parameters to the
* constructor of the method scoped class, where it passes the values * constructor of the method scoped class, where it passes the values
* of the local varaiables. If a method scoped class has more than * of the local varaiables. If a method scoped class has more than
* one constructor, each gets this hidden parameters. These hidden * one constructor, each gets this hidden parameters. These hidden
* parameters are the outerValues, because they are used to transport * 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.</p>
* *
* Unfortunately there is no definite way to distinguish this outer * <p>Unfortunately there is no definite way to distinguish this outer
* value parameters from the real parameters, so jode has to do a * value parameters from the real parameters, so jode has to do a
* guess: It first assumes that everything is an outer value parameter * guess: It first assumes that everything is an outer value parameter
* added by the compiler and if this leads to contradiction shrinks * added by the compiler and if this leads to contradiction shrinks
* the count of these parameters. A contradiction can occur, because * 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.</p>
* *
* On the other hand the TransformConstructor class assumes at some * <p>On the other hand the TransformConstructor class assumes at some
* point that some parameters are outer values. If later a * 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.</p>
* *
* Every class interested in outer values, may register itself as * <p>Every class interested in outer values, may register itself as
* OuterValueListener. It will then be notified every time the outer * OuterValueListener. It will then be notified every time the outer
* values shrink. Sometimes there are real listener queues: if * values shrink. Sometimes there are real listener queues: if
* another method scoped class creates instances of the first in its * another method scoped class creates instances of the first in its
@ -64,27 +64,25 @@ import java.util.Enumeration;
* add a listener. If later a constructor invokation for the second * add a listener. If later a constructor invokation for the second
* class is found, where a parameter does not have the right outer * class is found, where a parameter does not have the right outer
* value, the listener will also shrink the outer values list of the * value, the listener will also shrink the outer values list of the
* first class. * first class.</p>
* *
* A non static _class_ scoped class (i.e. a normal inner class) also * <p>A non static _class_ scoped class (i.e. a normal inner class) also
* has a hidden parameter, namely the instance of its outer class. * has a hidden parameter, namely the instance of its outer class.
* This hidden parameter is not considered as outer value though. * This hidden parameter is not considered as outer value though.
* Note that you can even explicitly invoke the constructor with a * Note that you can even explicitly invoke the constructor with a
* different outer class instance, by using the * different outer class instance, by using the
* <code>outerInstance.new InnerClass()</code> construct. This * <code>outerInstance.new InnerClass()</code> construct. This
* exception doesn't apply to method scoped classes, though. * exception doesn't apply to method scoped classes, though.</p>
* *
* Anonymous classes can of course also extend class or method scoped * <p>Anonymous classes can of course also extend class or method scoped
* classes. If they are compiled by jikes the constructor takes as * classes. If they are compiled by jikes the constructor takes as
* last parameter the outer instance of its super class. This should * last parameter the outer instance of its super class. This should
* really be the first parameter just after the outerValues, as it * really be the first parameter just after the outerValues, as it
* is under javac. We mark such classes as jikesAnonymousInner. This * is under javac. We mark such classes as jikesAnonymousInner. This
* is done in the initialize() pass. * is done in the initialize() pass.</p>
* *
* @see #shrinkOuterValues
* @see #addOuterValueListener * @see #addOuterValueListener
* @since 1.0.93 * @since 1.0.93 */
*/
public class OuterValues public class OuterValues
{ {
private ClassAnalyzer clazzAnalyzer; private ClassAnalyzer clazzAnalyzer;
@ -331,7 +329,6 @@ public class OuterValues
public void setCount(int newHeadCount) { public void setCount(int newHeadCount) {
if (newHeadCount >= headCount) if (newHeadCount >= headCount)
return; return;
headCount = newHeadCount;
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0) { & GlobalOptions.DEBUG_CONSTRS) != 0) {
@ -339,6 +336,7 @@ public class OuterValues
new Throwable().printStackTrace(GlobalOptions.err); new Throwable().printStackTrace(GlobalOptions.err);
} }
headCount = newHeadCount;
if (newHeadCount < headMinCount) { if (newHeadCount < headMinCount) {
GlobalOptions.err.println GlobalOptions.err.println
("WARNING: something got wrong with scoped class " ("WARNING: something got wrong with scoped class "
@ -374,5 +372,3 @@ public class OuterValues
return sb.append("]").toString(); return sb.append("]").toString();
} }
} }

@ -30,8 +30,9 @@ import jode.type.*;
public class TabbedPrintWriter { public class TabbedPrintWriter {
/* The indentation size. */ /* The indentation size. */
private int indentsize; 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 tabWidth;
private int style;
private int lineWidth; private int lineWidth;
private int currentIndent = 0; private int currentIndent = 0;
private String indentStr = ""; private String indentStr = "";
@ -39,40 +40,48 @@ public class TabbedPrintWriter {
private ImportHandler imports; private ImportHandler imports;
private Stack scopes = new Stack(); 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 StringBuffer currentLine;
private BreakPoint currentBP; private BreakPoint currentBP;
public final static int EXPL_PAREN = 0; public final static int EXPL_PAREN = 0;
public final static int NO_PAREN = 1; public final static int NO_PAREN = 1;
public final static int IMPL_PAREN = 2; public final static int IMPL_PAREN = 2;
public final static int DONT_BREAK = 3; 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. * Convert the numeric indentation to a string.
*/ */
protected String makeIndentStr(int indent) { 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) if (indent < 0)
return "NEGATIVEINDENT"+indent; return "NEGATIVEINDENT"+indent;
int tabs = indent / tabWidth; int tabs = indent / tabWidth;
indent -= tabs * tabWidth; indent -= tabs * tabWidth;
if (tabs <= 20 && indent <= 20) { if (tabs <= FASTINDENT) {
/* The fast way. */ /* The fast way. */
return tabSpaceString.substring(20 - tabs, 20 + indent); return tabSpaceString.substring(FASTINDENT - tabs,
FASTINDENT + indent);
} else { } else {
/* the not so fast way */ /* the not so fast way */
StringBuffer sb = new StringBuffer(tabs + indent); StringBuffer sb = new StringBuffer(tabs + indent);
while (tabs > 20) { while (tabs > FASTINDENT) {
sb.append(tabSpaceString.substring(0,20)); sb.append(tabSpaceString.substring(0, FASTINDENT));
tabs -= 20; tabs -= 20;
} }
sb.append(tabSpaceString.substring(0, tabs)); sb.append(tabSpaceString.substring(FASTINDENT - tabs,
while (indent > 20) { FASTINDENT + indent));
sb.append(tabSpaceString.substring(20));
indent -= 20;
}
sb.append(tabSpaceString.substring(40 - indent));
return sb.toString(); return sb.toString();
} }
} }
@ -101,7 +110,7 @@ public class TabbedPrintWriter {
public void startOp(int opts, int penalty, int pos) { public void startOp(int opts, int penalty, int pos) {
if (startPos != -1) { if (startPos != -1) {
System.err.println("WARNING: missing breakOp"); GlobalOptions.err.println("WARNING: missing breakOp");
Thread.dumpStack(); Thread.dumpStack();
return; return;
} }
@ -119,17 +128,14 @@ public class TabbedPrintWriter {
public void endOp(int pos) { public void endOp(int pos) {
endPos = pos; endPos = pos;
if (childBPs.size() == 1) { if (childBPs.size() == 1) {
BreakPoint child = /* There is no breakpoint in this op, replace this with
(BreakPoint) currentBP.childBPs.elementAt(0); * our child, if possible.
if (child.startPos == -1) { */
startPos = endPos = -1; BreakPoint child = (BreakPoint) childBPs.elementAt(0);
childBPs = null; startPos = child.startPos;
} else if (child.startPos == currentBP.startPos endPos = child.endPos;
&& child.endPos == currentBP.endPos) { breakPenalty = child.breakPenalty;
if (options == DONT_BREAK) childBPs = child.childBPs;
options = child.options;
childBPs = child.childBPs;
}
} }
} }
@ -217,7 +223,7 @@ public class TabbedPrintWriter {
} }
public BreakPoint commitMinPenalty(int space, int lastSpace, public BreakPoint commitMinPenalty(int space, int lastSpace,
int minPenalty) { int minPenalty) {
if (startPos == -1 || lastSpace > endPos - startPos if (startPos == -1 || lastSpace > endPos - startPos
|| minPenalty == 10 * (endPos - startPos - lastSpace)) { || minPenalty == 10 * (endPos - startPos - lastSpace)) {
/* We don't have to break anything */ /* We don't have to break anything */
@ -231,8 +237,6 @@ public class TabbedPrintWriter {
/* penalty if we are breaking the line here. */ /* penalty if we are breaking the line here. */
int breakPen int breakPen
= getBreakPenalty(space, lastSpace, minPenalty + 1); = getBreakPenalty(space, lastSpace, minPenalty + 1);
// pw.print("commit[bp="+breakPen+";"+minPenalty+";"
// +space+","+lastSpace+"]");
if (minPenalty == breakPen) { if (minPenalty == breakPen) {
commitBreakPenalty(space, lastSpace, breakPen); commitBreakPenalty(space, lastSpace, breakPen);
return this; return this;
@ -256,16 +260,11 @@ public class TabbedPrintWriter {
return child; return child;
} }
} }
pw.println("XXXXXXXXXXX CAN'T COMMIT"); throw new IllegalStateException("Can't commit line break!");
startPos = -1;
childBPs = null;
return this;
} }
public int getMinPenalty(int space, int lastSpace, int minPenalty) { public int getMinPenalty(int space, int lastSpace, int minPenalty) {
// pw.print("getMinPenalty["+startPos+","+endPos+"]("+space+","+lastSpace+","+minPenalty+") ");
if (10 * -lastSpace >= minPenalty) { if (10 * -lastSpace >= minPenalty) {
// pw.println("= minPenalty");
return minPenalty; return minPenalty;
} }
@ -273,20 +272,16 @@ public class TabbedPrintWriter {
return 10 * -lastSpace; return 10 * -lastSpace;
if (lastSpace > endPos - startPos) { if (lastSpace > endPos - startPos) {
// pw.println("= NULL");
return 0; return 0;
} }
if (minPenalty <= 1) { if (minPenalty <= 1) {
// pw.println("= ONE");
return minPenalty; return minPenalty;
} }
if (minPenalty > 10 * (endPos - startPos - lastSpace)) if (minPenalty > 10 * (endPos - startPos - lastSpace))
minPenalty = 10 * (endPos - startPos - lastSpace); minPenalty = 10 * (endPos - startPos - lastSpace);
// pw.print("[mp="+minPenalty+"]");
int size = childBPs.size(); int size = childBPs.size();
if (size == 0) if (size == 0)
return minPenalty; return minPenalty;
@ -294,7 +289,6 @@ public class TabbedPrintWriter {
if (size > 1 && options != DONT_BREAK) { if (size > 1 && options != DONT_BREAK) {
/* penalty if we are breaking at this level. */ /* penalty if we are breaking at this level. */
minPenalty = getBreakPenalty(space, lastSpace, minPenalty); minPenalty = getBreakPenalty(space, lastSpace, minPenalty);
// pw.print("[bp="+minPenalty+"]");
} }
/* penalty if we are breaking only one child */ /* penalty if we are breaking only one child */
@ -308,15 +302,11 @@ public class TabbedPrintWriter {
lastSpace - front - tail, lastSpace - front - tail,
minPenalty - penalty); minPenalty - penalty);
} }
// pw.println("= "+minPenalty);
return minPenalty; return minPenalty;
} }
public void commitBreakPenalty(int space, int lastSpace, public void commitBreakPenalty(int space, int lastSpace,
int minPenalty) { int minPenalty) {
// pw.println("commitBreakPenalty: "+startPos+","+endPos+";"
// +space+","+lastSpace+";"+minPenalty);
if (options == IMPL_PAREN) { if (options == IMPL_PAREN) {
space--; space--;
lastSpace -= 2; lastSpace -= 2;
@ -469,42 +459,75 @@ public class TabbedPrintWriter {
} }
public TabbedPrintWriter (OutputStream os, ImportHandler imports, public TabbedPrintWriter (OutputStream os, ImportHandler imports,
boolean autoFlush) { boolean autoFlush, int style,
int indentSize, int tabWidth, int lineWidth) {
pw = new PrintWriter(os, autoFlush); pw = new PrintWriter(os, autoFlush);
this.imports = imports; this.imports = imports;
this.style = style;
this.indentsize = indentSize;
this.tabWidth = tabWidth;
this.lineWidth = lineWidth;
init(); init();
} }
public TabbedPrintWriter (Writer os, ImportHandler imports, public TabbedPrintWriter (Writer os, ImportHandler imports,
boolean autoFlush) { boolean autoFlush, int style,
int indentSize, int tabWidth, int lineWidth) {
pw = new PrintWriter(os, autoFlush); pw = new PrintWriter(os, autoFlush);
this.imports = imports; this.imports = imports;
this.style = style;
this.indentsize = indentSize;
this.tabWidth = tabWidth;
this.lineWidth = lineWidth;
init(); 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) { 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) { public TabbedPrintWriter (Writer os, ImportHandler imports) {
this(os, imports, true); this(os, imports, true, BRACE_AT_EOL, 4, 8, 79);
} }
public TabbedPrintWriter (OutputStream os) { public TabbedPrintWriter (OutputStream os) {
this(os, null); this(os, null, true, BRACE_AT_EOL, 4, 8, 79);
} }
public TabbedPrintWriter (Writer os) { public TabbedPrintWriter (Writer os) {
this(os, null); this(os, null, true, BRACE_AT_EOL, 4, 8, 79);
} }
public void init() { private void init() {
this.indentsize = (Options.outputStyle & Options.TAB_SIZE_MASK);
this.tabWidth = 8;
this.lineWidth = 79;
currentLine = new StringBuffer(); currentLine = new StringBuffer();
currentBP = new BreakPoint(null, 0); currentBP = new BreakPoint(null, 0);
currentBP.startOp(DONT_BREAK, 1, 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() { public void tab() {
@ -680,9 +703,19 @@ public class TabbedPrintWriter {
public String getTypeString(Type type) { public String getTypeString(Type type) {
if (type instanceof ArrayType) if (type instanceof ArrayType)
return getTypeString(((ArrayType) type).getElementType()) + "[]"; return getTypeString(((ArrayType) type).getElementType()) + "[]";
else if (type instanceof ClassInterfacesType) { else if (type instanceof ClassInfoType) {
ClassInfo clazz = ((ClassInterfacesType) type).getClassInfo(); ClassInfo clazz = ((ClassInfoType) type).getClassInfo();
return getClassString(clazz, Scope.CLASSNAME); 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) } else if (type instanceof NullType)
return "Object"; return "Object";
else else
@ -695,7 +728,7 @@ public class TabbedPrintWriter {
* brace. It doesn't do a tab stop after opening the brace. * brace. It doesn't do a tab stop after opening the brace.
*/ */
public void openBrace() { public void openBrace() {
if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) { if ((style & BRACE_AT_EOL) != 0) {
print(currentLine.length() > 0 ? " {" : "{"); print(currentLine.length() > 0 ? " {" : "{");
println(); println();
} else { } else {
@ -713,7 +746,7 @@ public class TabbedPrintWriter {
* brace. It doesn't do a tab stop after opening the brace. * brace. It doesn't do a tab stop after opening the brace.
*/ */
public void openBraceNoSpace() { public void openBraceNoSpace() {
if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) if ((style & BRACE_AT_EOL) != 0)
println("{"); println("{");
else { else {
if (currentLine.length() > 0) if (currentLine.length() > 0)
@ -725,7 +758,7 @@ public class TabbedPrintWriter {
} }
public void closeBraceContinue() { public void closeBraceContinue() {
if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) if ((style & BRACE_AT_EOL) != 0)
print("} "); print("} ");
else { else {
println("}"); println("}");
@ -735,7 +768,7 @@ public class TabbedPrintWriter {
} }
public void closeBraceNoSpace() { public void closeBraceNoSpace() {
if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) if ((style & BRACE_AT_EOL) != 0)
print("}"); print("}");
else { else {
println("}"); println("}");
@ -745,7 +778,7 @@ public class TabbedPrintWriter {
} }
public void closeBrace() { public void closeBrace() {
if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) if ((style & BRACE_AT_EOL) != 0)
println("}"); println("}");
else { else {
println("}"); println("}");

@ -301,6 +301,7 @@ public abstract class Expression {
dumpExpression(writer); dumpExpression(writer);
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
writer.print("(RUNTIME ERROR IN EXPRESSION)"); writer.print("(RUNTIME ERROR IN EXPRESSION)");
ex.printStackTrace();
} }
if (needEndOp2) { if (needEndOp2) {

@ -21,9 +21,10 @@ package jode.expr;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.type.Type; import jode.type.Type;
import jode.type.NullType; import jode.type.NullType;
import jode.type.ClassInterfacesType; import jode.type.ClassInfoType;
import jode.bytecode.FieldInfo; import jode.bytecode.FieldInfo;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.ClassPath;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
import jode.decompiler.ClassAnalyzer; import jode.decompiler.ClassAnalyzer;
@ -48,14 +49,19 @@ public abstract class FieldOperator extends Operator {
Reference ref; Reference ref;
Type classType; Type classType;
ClassInfo classInfo; ClassInfo classInfo;
ClassPath classPath;
String callerPackage; String callerPackage;
public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag, public FieldOperator(MethodAnalyzer methodAnalyzer, boolean staticFlag,
Reference ref) { Reference ref) {
super(Type.tType(ref.getType())); super(Type.tUnknown);
this.classPath = methodAnalyzer.getClassAnalyzer().getClassPath();
this.methodAnalyzer = methodAnalyzer; this.methodAnalyzer = methodAnalyzer;
this.staticFlag = staticFlag; 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; this.ref = ref;
if (staticFlag) if (staticFlag)
methodAnalyzer.useType(classType); methodAnalyzer.useType(classType);
@ -64,8 +70,8 @@ public abstract class FieldOperator extends Operator {
callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName(); callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName();
int dot = callerPackage.lastIndexOf('.'); int dot = callerPackage.lastIndexOf('.');
callerPackage = callerPackage.substring(0, dot); callerPackage = callerPackage.substring(0, dot);
if (classType instanceof ClassInterfacesType) { if (classType instanceof ClassInfoType) {
classInfo = ((ClassInterfacesType) classType).getClassInfo(); classInfo = ((ClassInfoType) classType).getClassInfo();
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
|| (Options.options & Options.OPTION_INNER) != 0) { || (Options.options & Options.OPTION_INNER) != 0) {
try { try {
@ -111,7 +117,7 @@ public abstract class FieldOperator extends Operator {
while (true) { while (true) {
if (clazz == ana.getClazz()) { if (clazz == ana.getClazz()) {
int field = ana.getFieldIndex int field = ana.getFieldIndex
(ref.getName(), Type.tType(ref.getType())); (ref.getName(), Type.tType(classPath, ref.getType()));
if (field >= 0) if (field >= 0)
return ana.getField(field); return ana.getField(field);
return null; return null;
@ -135,7 +141,7 @@ public abstract class FieldOperator extends Operator {
} }
public Type getFieldType() { public Type getFieldType() {
return Type.tType(ref.getType()); return Type.tType(classPath, ref.getType());
} }
private FieldInfo[] loadFields(ClassInfo clazz) { private FieldInfo[] loadFields(ClassInfo clazz) {
@ -156,12 +162,12 @@ public abstract class FieldOperator extends Operator {
public boolean needsCast(Type type) { public boolean needsCast(Type type) {
if (type instanceof NullType) if (type instanceof NullType)
return true; return true;
if (!(type instanceof ClassInterfacesType if (!(type instanceof ClassInfoType
&& classType instanceof ClassInterfacesType)) && classType instanceof ClassInfoType))
return false; return false;
ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo(); ClassInfo clazz = ((ClassInfoType) classType).getClassInfo();
ClassInfo parClazz = ((ClassInterfacesType) type).getClassInfo(); ClassInfo parClazz = ((ClassInfoType) type).getClassInfo();
while (clazz != parClazz && clazz != null) { while (clazz != parClazz && clazz != null) {
FieldInfo[] fields = parClazz.getFields(); FieldInfo[] fields = parClazz.getFields();
for (int i = 0; i < fields.length; i++) { for (int i = 0; i < fields.length; i++) {

@ -19,6 +19,7 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassPath;
import jode.decompiler.FieldAnalyzer; import jode.decompiler.FieldAnalyzer;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
@ -86,10 +87,11 @@ public class IfThenElseOperator extends Operator {
.equals(Type.tString))) { .equals(Type.tString))) {
String clazz = (String) String clazz = (String)
((ConstOperator)invoke.subExpressions[0]).getValue(); ((ConstOperator)invoke.subExpressions[0]).getValue();
ClassPath cp = field.getClassAnalyzer().getClassPath();
if (field.setClassConstant(clazz)) if (field.setClassConstant(clazz))
return new ClassFieldOperator(clazz.charAt(0) == '[' return new ClassFieldOperator
? Type.tType(clazz) (clazz.charAt(0) == '['
: Type.tClass(clazz)); ? Type.tType(cp, clazz) : Type.tClass(cp, clazz));
} }
} }
} }

@ -63,9 +63,10 @@ public final class InvokeOperator extends Operator
MethodType methodType; MethodType methodType;
String methodName; String methodName;
int skippedArgs; int skippedArgs;
Type classType; ClassType classType;
Type[] hints; Type[] hints;
ClassInfo classInfo; ClassInfo classInfo;
ClassPath classPath;
String callerPackage; String callerPackage;
/** /**
@ -105,6 +106,20 @@ public final class InvokeOperator extends Operator
Type[] hint0C = new Type[] { null, tCharHint }; Type[] hint0C = new Type[] { null, tCharHint };
Type[] hint0C0 = new Type[] { null, tCharHint, null }; 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 Map hintString0CMap = new SimpleMap
(Collections.singleton (Collections.singleton
(new SimpleMap.SimpleEntry(Type.tString, hint0C))); (new SimpleMap.SimpleEntry(Type.tString, hint0C)));
@ -118,24 +133,26 @@ public final class InvokeOperator extends Operator
hintTypes.put("write.(I)V", new SimpleMap hintTypes.put("write.(I)V", new SimpleMap
(Collections.singleton (Collections.singleton
(new SimpleMap.SimpleEntry (new SimpleMap.SimpleEntry
(Type.tClass("java.io.Writer"), hint0C)))); (tWriter, hint0C))));
hintTypes.put("read.()I", new SimpleMap hintTypes.put("read.()I", new SimpleMap
(Collections.singleton (Collections.singleton
(new SimpleMap.SimpleEntry (new SimpleMap.SimpleEntry
(Type.tClass("java.io.Reader"), hintC)))); (tReader, hintC))));
hintTypes.put("unread.(I)V", new SimpleMap hintTypes.put("unread.(I)V", new SimpleMap
(Collections.singleton (Collections.singleton
(new SimpleMap.SimpleEntry (new SimpleMap.SimpleEntry
(Type.tClass("java.io.PushbackReader"), hint0C)))); (tPBReader, hint0C))));
} }
public InvokeOperator(MethodAnalyzer methodAnalyzer, public InvokeOperator(MethodAnalyzer methodAnalyzer,
int methodFlag, Reference reference) { int methodFlag, Reference reference) {
super(Type.tUnknown, 0); 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.methodName = reference.getName();
this.classType = Type.tType(reference.getClazz()); this.classType = (ClassType)
Type.tType(classPath, reference.getClazz());
this.hints = null; this.hints = null;
Map allHints = (Map) hintTypes.get(methodName+"."+methodType); Map allHints = (Map) hintTypes.get(methodName+"."+methodType);
if (allHints != null) { if (allHints != null) {
@ -161,8 +178,8 @@ public final class InvokeOperator extends Operator
callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName(); callerPackage = methodAnalyzer.getClassAnalyzer().getClass().getName();
int dot = callerPackage.lastIndexOf('.'); int dot = callerPackage.lastIndexOf('.');
callerPackage = callerPackage.substring(0, dot); callerPackage = callerPackage.substring(0, dot);
if (classType instanceof ClassInterfacesType) { if (classType instanceof ClassInfoType) {
classInfo = ((ClassInterfacesType) classType).getClassInfo(); classInfo = ((ClassInfoType) classType).getClassInfo();
if ((Options.options & Options.OPTION_ANON) != 0 if ((Options.options & Options.OPTION_ANON) != 0
|| (Options.options & Options.OPTION_INNER) != 0) { || (Options.options & Options.OPTION_INNER) != 0) {
try { try {
@ -175,6 +192,10 @@ public final class InvokeOperator extends Operator
} }
} }
public final ClassPath getClassPath() {
return classPath;
}
public final boolean isStatic() { public final boolean isStatic() {
return methodFlag == STATIC; return methodFlag == STATIC;
} }
@ -207,13 +228,13 @@ public final class InvokeOperator extends Operator
public void updateSubTypes() { public void updateSubTypes() {
int offset = 0; int offset = 0;
if (!isStatic()) { if (!isStatic()) {
subExpressions[offset++].setType(Type.tSubType(getClassType())); subExpressions[offset++].setType(getClassType().getSubType());
} }
Type[] paramTypes = methodType.getParameterTypes(); Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i < paramTypes.length; i++) { for (int i=0; i < paramTypes.length; i++) {
Type pType = (hints != null && hints[i+1] != null) Type pType = (hints != null && hints[i+1] != null)
? hints[i+1] : paramTypes[i]; ? 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. * outer instance.
*/ */
public boolean isOuter() { public boolean isOuter() {
if (classType instanceof ClassInterfacesType) { if (classInfo != null) {
ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo();
ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer(); ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
while (true) { while (true) {
if (clazz == ana.getClazz()) if (classInfo == ana.getClazz())
return true; return true;
if (ana.getParent() == null) if (ana.getParent() == null)
break; break;
@ -362,17 +382,8 @@ public final class InvokeOperator extends Operator
* Checks, whether this is a call of a method from the super class. * Checks, whether this is a call of a method from the super class.
*/ */
public boolean isSuperOrThis() { public boolean isSuperOrThis() {
ClassInfo clazz = getClassInfo(); return classType.maybeSubTypeOf
if (clazz != null) { (Type.tClass(methodAnalyzer.getClazz()));
try {
return clazz.superClassOf(methodAnalyzer.getClazz());
} catch (IOException ex) {
/* Assume it is not a super class. This will print
* a warning.
*/
}
}
return false;
} }
public boolean isConstant() { public boolean isConstant() {
@ -664,11 +675,11 @@ public final class InvokeOperator extends Operator
realClassType = paramTypes[0]; realClassType = paramTypes[0];
} }
if (!(realClassType instanceof ClassInterfacesType)) { if (!(realClassType instanceof ClassInfoType)) {
/* Arrays don't have overloaded methods, all okay */ /* Arrays don't have overloaded methods, all okay */
return false; return false;
} }
ClassInfo clazz = ((ClassInterfacesType) realClassType).getClassInfo(); ClassInfo clazz = ((ClassInfoType) realClassType).getClassInfo();
int offset = skippedArgs; int offset = skippedArgs;
Type[] myParamTypes = methodType.getParameterTypes(); Type[] myParamTypes = methodType.getParameterTypes();
@ -687,7 +698,8 @@ public final class InvokeOperator extends Operator
continue next_method; continue next_method;
Type[] otherParamTypes Type[] otherParamTypes
= Type.tMethod(methods[i].getType()).getParameterTypes(); = Type.tMethod(classPath, methods[i].getType())
.getParameterTypes();
if (otherParamTypes.length != myParamTypes.length) { if (otherParamTypes.length != myParamTypes.length) {
/* parameter count doesn't match*/ /* parameter count doesn't match*/
continue next_method; 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 && clazz.getOuterClass() != null
&& !Modifier.isStatic(clazz.getModifiers())) { && !Modifier.isStatic(clazz.getModifiers())) {
@ -873,8 +886,7 @@ public final class InvokeOperator extends Operator
/* clazz != null, since an array doesn't have a constructor */ /* clazz != null, since an array doesn't have a constructor */
clazzAna = methodAnalyzer.getClassAnalyzer(clazz); clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
if ((~Options.options & if ((Options.options & Options.OPTION_ANON) != 0
(Options.OPTION_ANON | Options.OPTION_CONTRAFO)) == 0
&& clazzAna != null && clazz.isMethodScoped()) { && clazzAna != null && clazz.isMethodScoped()) {
/* This is a known method scoped class, skip the outerValues */ /* This is a known method scoped class, skip the outerValues */

@ -57,6 +57,7 @@ public class NewArrayOperator extends Operator {
writer.print("new "); writer.print("new ");
writer.printType(flat.getHint()); writer.printType(flat.getHint());
for (int i=0; i< depth; i++) { for (int i=0; i< depth; i++) {
writer.breakOp();
writer.print("["); writer.print("[");
if (i < subExpressions.length) if (i < subExpressions.length)
subExpressions[i].dumpExpression(writer, 0); subExpressions[i].dumpExpression(writer, 0);

@ -19,6 +19,7 @@
package jode.flow; package jode.flow;
import jode.expr.*; import jode.expr.*;
import jode.bytecode.ClassPath;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
@ -71,10 +72,11 @@ public class CreateClassField {
&& ((ConstOperator)param).getValue() instanceof String) { && ((ConstOperator)param).getValue() instanceof String) {
String clazz = (String) ((ConstOperator)param).getValue(); String clazz = (String) ((ConstOperator)param).getValue();
if (put.getField().setClassConstant(clazz)) { if (put.getField().setClassConstant(clazz)) {
ClassPath cp = invoke.getClassPath();
cmp.setSubExpressions cmp.setSubExpressions
(0, new ClassFieldOperator(clazz.charAt(0) == '[' (0, new ClassFieldOperator(clazz.charAt(0) == '['
? Type.tType(clazz) ? Type.tType(cp, clazz)
: Type.tClass(clazz))); : Type.tClass(cp, clazz)));
EmptyBlock empty = new EmptyBlock(); EmptyBlock empty = new EmptyBlock();
empty.moveJump(ifBlock.thenBlock.jump); empty.moveJump(ifBlock.thenBlock.jump);
ifBlock.setThenBlock(empty); ifBlock.setThenBlock(empty);

@ -806,7 +806,7 @@ public class FlowBlock {
try { try {
if (block.outer != null || block.flowBlock != this) { 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(); block.checkConsistent();
@ -869,7 +869,7 @@ public class FlowBlock {
} }
public void prependBlock(StructuredBlock insertBlock) { public void prependBlock(StructuredBlock insertBlock) {
lastModified = insertBlock.appendBlock(block); lastModified = block = insertBlock.appendBlock(block);
SlotSet blockIn = new SlotSet(); SlotSet blockIn = new SlotSet();
SlotSet blockKill = new SlotSet(); SlotSet blockKill = new SlotSet();
VariableSet blockGen = new VariableSet(); VariableSet blockGen = new VariableSet();
@ -1405,8 +1405,9 @@ public class FlowBlock {
boolean predOutOfRange = false; boolean predOutOfRange = false;
for (Iterator i = succ.predecessors.iterator(); for (Iterator i = succ.predecessors.iterator();
i.hasNext(); ) { i.hasNext(); ) {
int predBlockNr = ((FlowBlock)i.next()).blockNr; FlowBlock pred = (FlowBlock)i.next();
if (predBlockNr < start || predBlockNr >= end) { if (pred == null /* the start marker */
|| pred.blockNr < start || pred.blockNr >= end) {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
@ -1552,10 +1553,14 @@ public class FlowBlock {
/** /**
* Mark the flow block as first flow block in a method. * Mark the flow block as first flow block in a method.
*/ */
public void makeStartBlock() { public void addStartPred() {
predecessors.add(null); predecessors.add(null);
} }
public void removeStartPred() {
predecessors.remove(null);
}
public void removeSuccessor(Jump jump) { public void removeSuccessor(Jump jump) {
SuccessorInfo destInfo SuccessorInfo destInfo
= (SuccessorInfo) successors.get(jump.destination); = (SuccessorInfo) successors.get(jump.destination);
@ -1675,6 +1680,9 @@ public class FlowBlock {
private void promoteInSets() { private void promoteInSets() {
for (Iterator i = predecessors.iterator(); i.hasNext(); ) { for (Iterator i = predecessors.iterator(); i.hasNext(); ) {
FlowBlock pred = (FlowBlock) i.next(); FlowBlock pred = (FlowBlock) i.next();
/* Skip the start marker */
if (pred == null)
continue;
SuccessorInfo succInfo = (SuccessorInfo) pred.successors.get(this); SuccessorInfo succInfo = (SuccessorInfo) pred.successors.get(this);
/* First get the gen/kill sets of all jumps of predecessor /* First get the gen/kill sets of all jumps of predecessor
@ -1743,7 +1751,7 @@ public class FlowBlock {
public void dumpSource(TabbedPrintWriter writer) public void dumpSource(TabbedPrintWriter writer)
throws java.io.IOException throws java.io.IOException
{ {
if (predecessors.size() != 0) { if (predecessors.size() != 0) {
writer.untab(); writer.untab();
writer.println(getLabel()+":"); writer.println(getLabel()+":");
writer.tab(); writer.tab();

@ -99,8 +99,6 @@ public class TransformConstructors {
OuterValues outerValues; OuterValues outerValues;
boolean jikesAnonInner = false;
public TransformConstructors(ClassAnalyzer clazzAnalyzer, public TransformConstructors(ClassAnalyzer clazzAnalyzer,
boolean isStatic, MethodAnalyzer[] cons) { boolean isStatic, MethodAnalyzer[] cons) {
this.clazzAnalyzer = clazzAnalyzer; this.clazzAnalyzer = clazzAnalyzer;
@ -158,10 +156,16 @@ public class TransformConstructors {
return 1; return 1;
} }
public void lookForConstructorCall() { private void lookForConstructorCall() {
type01Count = cons.length; type01Count = cons.length;
for (int i=0; i< type01Count; ) { for (int i=0; i< type01Count; ) {
MethodAnalyzer current = cons[i]; 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(); FlowBlock header = cons[i].getMethodHeader();
/* Check that code block is fully analyzed */ /* Check that code block is fully analyzed */
if (header == null || !header.hasNoJumps()) if (header == null || !header.hasNoJumps())
@ -261,13 +265,14 @@ public class TransformConstructors {
/* slot counts from last slot down. */ /* slot counts from last slot down. */
int start = 1; int start = 1;
if (subExpr.length > 2) { if (subExpr.length >= 2) {
LocalLoadOperator llop = (LocalLoadOperator) subExpr[1]; LocalLoadOperator llop = (LocalLoadOperator) subExpr[1];
if (llop.getLocalInfo().getSlot() == slot) { if (llop.getLocalInfo().getSlot() == slot) {
jikesAnon = true; jikesAnon = true;
start++; start++;
// This is not an outer value. // This is not an outer value.
outerValues.setCount(params.length - 1);
minOuter--; minOuter--;
slot -= params[minOuter - 1].stackSize(); slot -= params[minOuter - 1].stackSize();
} }
@ -370,6 +375,7 @@ public class TransformConstructors {
private Expression renameJikesSuper(Expression expr, private Expression renameJikesSuper(Expression expr,
MethodAnalyzer methodAna, MethodAnalyzer methodAna,
Expression outer0,
int firstOuterSlot, int firstOuterSlot,
int firstParamSlot) { int firstParamSlot) {
if (expr instanceof LocalLoadOperator) { if (expr instanceof LocalLoadOperator) {
@ -377,7 +383,9 @@ public class TransformConstructors {
int slot = llop.getLocalInfo().getSlot(); int slot = llop.getLocalInfo().getSlot();
if (slot >= firstOuterSlot && slot < firstParamSlot) if (slot >= firstOuterSlot && slot < firstParamSlot)
return outerValues.getValueBySlot(slot); return outerValues.getValueBySlot(slot);
else { else if (slot == 1) {
return outer0;
} else {
Type[] paramTypes = methodAna.getType().getParameterTypes(); Type[] paramTypes = methodAna.getType().getParameterTypes();
int param; int param;
/* Adjust the slot */ /* Adjust the slot */
@ -394,7 +402,7 @@ public class TransformConstructors {
Expression subExpr[] = ((Operator)expr).getSubExpressions(); Expression subExpr[] = ((Operator)expr).getSubExpressions();
for (int i=0; i< subExpr.length; i++) { for (int i=0; i< subExpr.length; i++) {
Expression newSubExpr = Expression newSubExpr =
renameJikesSuper(subExpr[i], methodAna, renameJikesSuper(subExpr[i], methodAna, outer0,
firstOuterSlot, firstParamSlot); firstOuterSlot, firstParamSlot);
if (newSubExpr != subExpr[i]) if (newSubExpr != subExpr[i])
((Operator)expr).setSubExpressions(i, newSubExpr); ((Operator)expr).setSubExpressions(i, newSubExpr);
@ -403,7 +411,7 @@ public class TransformConstructors {
return expr; return expr;
} }
public void checkJikesContinuation() { private void checkJikesContinuation() {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0) & GlobalOptions.DEBUG_CONSTRS) != 0)
System.err.println("checkJikesContinuation: "+outerValues); System.err.println("checkJikesContinuation: "+outerValues);
@ -441,6 +449,7 @@ public class TransformConstructors {
Vector localLoads = null; Vector localLoads = null;
InstructionBlock superBlock = null; InstructionBlock superBlock = null;
InvokeOperator superInvoke = null;
if (i >= type0Count) { if (i >= type0Count) {
/* Extract the super() or this() call at the beginning /* Extract the super() or this() call at the beginning
* of the constructor * of the constructor
@ -452,15 +461,10 @@ public class TransformConstructors {
superBlock = (InstructionBlock) sb.getSubBlocks()[0]; superBlock = (InstructionBlock) sb.getSubBlocks()[0];
sb = sb.getSubBlocks()[1]; sb = sb.getSubBlocks()[1];
Expression superExpr = superBlock.getInstruction().simplify(); superInvoke = (InvokeOperator)
InvokeOperator superInvoke = (InvokeOperator) superExpr; superBlock.getInstruction().simplify();
superBlock.setInstruction(superInvoke); if (!checkJikesSuper(superInvoke))
continue constr_loop;
Expression[] subExpr = superInvoke.getSubExpressions();
for (int j=1; j< subExpr.length; j++) {
if (!checkJikesSuper(subExpr[j]))
continue constr_loop;
}
} }
if (!(sb instanceof InstructionBlock)) if (!(sb instanceof InstructionBlock))
@ -544,11 +548,19 @@ public class TransformConstructors {
/* Now move the constructor call. /* Now move the constructor call.
*/ */
if (superBlock != null) { if (superBlock != null) {
Expression newExpr = InvokeOperator newSuper = (InvokeOperator)
renameJikesSuper(superBlock.getInstruction(), methodAna, renameJikesSuper(superInvoke, methodAna, outer0,
firstOuterSlot, firstParamSlot); firstOuterSlot, firstParamSlot);
superBlock.removeBlock(); 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) { if (outer0 != null) {
methodAna.getParamInfo(1).setExpression(outer0); methodAna.getParamInfo(1).setExpression(outer0);
@ -573,7 +585,7 @@ public class TransformConstructors {
* @param expr the initializer to check * @param expr the initializer to check
* @return the transformed initializer or null if expr is not valid. * @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 LocalVarOperator) {
if (!(expr instanceof LocalLoadOperator)) { if (!(expr instanceof LocalLoadOperator)) {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
@ -608,6 +620,11 @@ public class TransformConstructors {
return expr; 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() { public void removeSynthInitializers() {
if ((Options.options & Options.OPTION_CONTRAFO) == 0 if ((Options.options & Options.OPTION_CONTRAFO) == 0
|| isStatic || type01Count == 0) || isStatic || type01Count == 0)
@ -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)) if (!(ib instanceof InstructionBlock))
return -1; return -1;
@ -787,7 +804,7 @@ public class TransformConstructors {
return field; return field;
} }
public void transformBlockInitializer(StructuredBlock block) { private void transformBlockInitializer(StructuredBlock block) {
StructuredBlock start = null; StructuredBlock start = null;
StructuredBlock tail = null; StructuredBlock tail = null;
int lastField = -1; int lastField = -1;
@ -804,7 +821,7 @@ public class TransformConstructors {
clazzAnalyzer.addBlockInitializer(lastField + 1, block); clazzAnalyzer.addBlockInitializer(lastField + 1, block);
} }
public boolean checkBlockInitializer(InvokeOperator invoke) { private boolean checkBlockInitializer(InvokeOperator invoke) {
if (!invoke.isThis() if (!invoke.isThis()
|| invoke.getFreeOperandCount() != 0) || invoke.getFreeOperandCount() != 0)
return false; return false;
@ -829,11 +846,63 @@ public class TransformConstructors {
return true; return true;
} }
private void removeDefaultSuper() {
/* Checks if superInvoke is the default super call.
*/
private boolean isDefaultSuper(InvokeOperator superInvoke) {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0) & GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("removeDefaultSuper of " GlobalOptions.err.println("isDefaultSuper: "+superInvoke);
+ clazzAnalyzer.getClazz());
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. /* Check if we can remove the super() call of type1 constructors.
* This transforms a type1 constructor in a type0 constructor. * This transforms a type1 constructor in a type0 constructor.
*/ */
@ -841,11 +910,6 @@ public class TransformConstructors {
MethodAnalyzer current = cons[i]; MethodAnalyzer current = cons[i];
FlowBlock header = cons[i].getMethodHeader(); FlowBlock header = cons[i].getMethodHeader();
StructuredBlock body = header.block; StructuredBlock body = header.block;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
GlobalOptions.err.println("constr "+i+": "+body);
InstructionBlock ib; InstructionBlock ib;
if (body instanceof InstructionBlock) if (body instanceof InstructionBlock)
ib = (InstructionBlock) body; ib = (InstructionBlock) body;
@ -854,45 +918,14 @@ public class TransformConstructors {
InvokeOperator superInvoke = (InvokeOperator) InvokeOperator superInvoke = (InvokeOperator)
ib.getInstruction().simplify(); ib.getInstruction().simplify();
ClassInfo superClazz = superInvoke.getClassInfo(); if (isDefaultSuper(superInvoke)) {
int superParamCount = superInvoke.getSubExpressions().length - 1; ib.removeBlock();
if (i > type0Count) {
if (superClazz != null) { cons[i] = cons[type0Count];
try { cons[type0Count] = current;
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 */
} }
type0Count++;
} }
ib.removeBlock();
if (i > type0Count) {
cons[i] = cons[type0Count];
cons[type0Count] = current;
}
type0Count++;
} }
} }
@ -1010,23 +1043,17 @@ public class TransformConstructors {
|| cons.length == 0) || cons.length == 0)
return; return;
removeDefaultSuper();
removeInitializers(); removeInitializers();
checkJikesContinuation(); checkJikesContinuation();
if (outerValues != null) { if (outerValues != null) {
/* Now tell all constructors the value of outerValues parameters /* Now tell all constructors the value of outerValues parameters */
* and simplify them again.
*/
for (int i=0; i< cons.length; i++) { for (int i=0; i< cons.length; i++) {
for (int j = 0; j < outerValues.getCount(); j++) for (int j = 0; j < outerValues.getCount(); j++)
cons[i].getParamInfo(j+1) cons[i].getParamInfo(j+1)
.setExpression(outerValues.getValue(j)); .setExpression(outerValues.getValue(j));
// if (outerValues.isJikesAnonymousConstructor()) {
// /*XXX???*/
// }
cons[i].getMethodHeader().simplify();
} }
} }
removeDefaultSuper();
} }
} }

@ -169,8 +169,8 @@ public class TryBlock extends StructuredBlock {
if (instr.isVoid() || instr.getFreeOperandCount() != 0 if (instr.isVoid() || instr.getFreeOperandCount() != 0
|| !(instr instanceof InvokeOperator) || !(instr instanceof InvokeOperator)
|| !(catchBlock.catchBlock instanceof ThrowBlock) || !(catchBlock.catchBlock instanceof ThrowBlock)
|| !(catchBlock.exceptionType.equals || !(((ClassType) catchBlock.exceptionType).getClassName().equals
(Type.tClass("java.lang.CloneNotSupportedException")))) ("java.lang.CloneNotSupportedException")))
return false; return false;
InvokeOperator arrayClone = (InvokeOperator) instr; InvokeOperator arrayClone = (InvokeOperator) instr;
@ -191,8 +191,9 @@ public class TryBlock extends StructuredBlock {
InvokeOperator throwOp = (InvokeOperator) throwExpr; InvokeOperator throwOp = (InvokeOperator) throwExpr;
if (!throwOp.isConstructor() if (!throwOp.isConstructor()
|| !(throwOp.getClassType() || !(throwOp.getClassType() instanceof ClassType)
.equals(Type.tClass("java.lang.InternalError"))) || !(((ClassType) throwOp.getClassType()).getClassName()
.equals("java.lang.InternalError"))
|| throwOp.getMethodType().getParameterTypes().length != 1) || throwOp.getMethodType().getParameterTypes().length != 1)
return false; return false;

@ -35,6 +35,7 @@ import java.io.IOException;
import java.util.BitSet; import java.util.BitSet;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -1302,7 +1303,7 @@ public class CodeVerifier implements Opcodes {
Instruction instr = null; Instruction instr = null;
Iterator iter = block.getInstructions().iterator(); Iterator iter = Arrays.asList(block.getInstructions()).iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
instr = (Instruction) iter.next(); instr = (Instruction) iter.next();
modelEffect(instr, info); modelEffect(instr, info);

@ -107,7 +107,7 @@ public class Interpreter implements Opcodes {
/* If block is over continue with the next block */ /* If block is over continue with the next block */
if (nextBlock == null) if (nextBlock == null)
return Void.TYPE; return Void.TYPE;
iter = nextBlock.getInstructions().iterator(); iter = Arrays.asList(nextBlock.getInstructions()).iterator();
succs = nextBlock.getSuccs(); succs = nextBlock.getSuccs();
handlers = nextBlock.getCatchers(); handlers = nextBlock.getCatchers();
nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; nextBlock = succs.length > 0 ? succs[succs.length - 1] : null;

@ -35,6 +35,7 @@ import jode.type.MethodType;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
///#enddef ///#enddef
@ -117,7 +118,7 @@ public class SyntheticAnalyzer implements Opcodes {
Block startBlock = bb.getStartBlock(); Block startBlock = bb.getStartBlock();
Handler[] excHandlers = bb.getExceptionHandlers(); Handler[] excHandlers = bb.getExceptionHandlers();
if (startBlock == null if (startBlock == null
|| startBlock.getInstructions().size() != 3 || startBlock.getInstructions().length != 3
|| excHandlers.length != 1 || excHandlers.length != 1
|| excHandlers[0].getStart() != startBlock || excHandlers[0].getStart() != startBlock
|| excHandlers[0].getEnd() != startBlock || excHandlers[0].getEnd() != startBlock
@ -126,8 +127,7 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
for (int i=0; i< 3; i++) { for (int i=0; i< 3; i++) {
Instruction instr = Instruction instr = startBlock.getInstructions()[i];
(Instruction) startBlock.getInstructions().get(i);
if (instr.getOpcode() != getClassOpcodes[i]) if (instr.getOpcode() != getClassOpcodes[i])
return false; return false;
if (getClassRefs[i] != null if (getClassRefs[i] != null
@ -138,12 +138,11 @@ public class SyntheticAnalyzer implements Opcodes {
} }
Block catchBlock = excHandlers[0].getCatcher(); Block catchBlock = excHandlers[0].getCatcher();
if (catchBlock.getInstructions().size() != 7) if (catchBlock.getInstructions().length != 7)
return false; return false;
int excSlot = -1; int excSlot = -1;
for (int i=0; i< 7; i++) { for (int i=0; i< 7; i++) {
Instruction instr = (Instruction) Instruction instr = catchBlock.getInstructions()[i];
catchBlock.getInstructions().get(i);
if (instr.getOpcode() != getClassOpcodes[3+i]) if (instr.getOpcode() != getClassOpcodes[3+i])
return false; return false;
if (getClassRefs[3+i] != null if (getClassRefs[3+i] != null
@ -177,7 +176,7 @@ public class SyntheticAnalyzer implements Opcodes {
if (succBlocks.length > 1 || if (succBlocks.length > 1 ||
(succBlocks.length == 1 && succBlocks[0] != null)) (succBlocks.length == 1 && succBlocks[0] != null))
return false; return false;
Iterator iter = startBlock.getInstructions().iterator(); Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator();
if (!iter.hasNext()) if (!iter.hasNext())
return false; return false;
Instruction instr = (Instruction) iter.next(); Instruction instr = (Instruction) iter.next();
@ -245,7 +244,8 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
MethodInfo refMethod MethodInfo refMethod
= classInfo.findMethod(ref.getName(), ref.getType()); = classInfo.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(ref.getType()); MethodType refType = Type.tMethod(classInfo.getClassPath(),
ref.getType());
if ((refMethod.getModifiers() & modifierMask) != if ((refMethod.getModifiers() & modifierMask) !=
(Modifier.PRIVATE | Modifier.STATIC) (Modifier.PRIVATE | Modifier.STATIC)
|| refType.getParameterTypes().length != params) || refType.getParameterTypes().length != params)
@ -291,7 +291,7 @@ public class SyntheticAnalyzer implements Opcodes {
if (succBlocks.length > 1 || if (succBlocks.length > 1 ||
(succBlocks.length == 1 && succBlocks[0] != null)) (succBlocks.length == 1 && succBlocks[0] != null))
return false; return false;
Iterator iter = startBlock.getInstructions().iterator(); Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator();
if (!iter.hasNext()) if (!iter.hasNext())
return false; return false;
@ -361,7 +361,8 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
MethodInfo refMethod MethodInfo refMethod
= classInfo.findMethod(ref.getName(), ref.getType()); = 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 if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE
|| refType.getParameterTypes().length != params) || refType.getParameterTypes().length != params)
return false; return false;
@ -398,7 +399,7 @@ public class SyntheticAnalyzer implements Opcodes {
Block[] succBlocks = startBlock.getSuccs(); Block[] succBlocks = startBlock.getSuccs();
if (succBlocks.length != 1 || succBlocks[0] != null) if (succBlocks.length != 1 || succBlocks[0] != null)
return false; return false;
Iterator iter = startBlock.getInstructions().iterator(); Iterator iter = Arrays.asList(startBlock.getInstructions()).iterator();
if (!iter.hasNext()) if (!iter.hasNext())
return false; return false;
@ -439,7 +440,8 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
MethodInfo refMethod MethodInfo refMethod
= classInfo.findMethod(ref.getName(), ref.getType()); = 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 if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE
|| !refMethod.getName().equals("<init>") || !refMethod.getName().equals("<init>")
|| unifyParam == -1 || unifyParam == -1

@ -25,6 +25,7 @@ import jode.bytecode.Reference;
import jode.obfuscator.modules.WildCard; import jode.obfuscator.modules.WildCard;
import jode.obfuscator.modules.MultiIdentifierMatcher; import jode.obfuscator.modules.MultiIdentifierMatcher;
import jode.obfuscator.modules.SimpleAnalyzer; import jode.obfuscator.modules.SimpleAnalyzer;
import jode.obfuscator.modules.IdentityRenamer;
import java.io.*; import java.io.*;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@ -427,27 +428,7 @@ public class ClassBundle implements OptionHandler {
if (postTrafos == null) if (postTrafos == null)
postTrafos = new CodeTransformer[0]; postTrafos = new CodeTransformer[0];
if (renamer == null) if (renamer == null)
renamer = new Renamer() { renamer = new IdentityRenamer();
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();
}
};
}
};
Runtime runtime = Runtime.getRuntime(); Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory(); long free = runtime.freeMemory();

@ -262,7 +262,7 @@ public class ClassIdentifier extends Identifier {
/* add a field serializableVersionUID if not existent */ /* add a field serializableVersionUID if not existent */
long serialVersion = calcSerialVersionUID(); long serialVersion = calcSerialVersionUID();
FieldInfo UIDField = new FieldInfo FieldInfo UIDField = new FieldInfo
(info, "serialVersionUID", "J", ("serialVersionUID", "J",
Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
UIDField.setConstant(new Long(serialVersion)); UIDField.setConstant(new Long(serialVersion));
FieldIdentifier UIDident = new FieldIdentifier(this, UIDField); FieldIdentifier UIDident = new FieldIdentifier(this, UIDField);
@ -452,6 +452,7 @@ public class ClassIdentifier extends Identifier {
} }
if (extraClasses != null) { if (extraClasses != null) {
for (int i=0; i < extraClasses.length; i++) { for (int i=0; i < extraClasses.length; i++) {
System.err.println("ec["+i+"]:"+extraClasses[i].getName());
Main.getClassBundle() Main.getClassBundle()
.getClassIdentifier(extraClasses[i].getName()); .getClassIdentifier(extraClasses[i].getName());
} }

@ -18,8 +18,8 @@
*/ */
package jode.obfuscator; package jode.obfuscator;
import jode.bytecode.BytecodeInfo; import jode.bytecode.BasicBlocks;
public interface CodeAnalyzer extends CodeTransformer { public interface CodeAnalyzer extends CodeTransformer {
public void analyzeCode(MethodIdentifier parent, BytecodeInfo bytecode); public void analyzeCode(MethodIdentifier parent, BasicBlocks bb);
} }

@ -18,8 +18,8 @@
*/ */
package jode.obfuscator; package jode.obfuscator;
import jode.bytecode.BytecodeInfo; import jode.bytecode.BasicBlocks;
public interface CodeTransformer { public interface CodeTransformer {
public void transformCode(BytecodeInfo bytecode); public void transformCode(BasicBlocks bb);
} }

@ -22,7 +22,7 @@ import jode.jvm.Interpreter;
import jode.jvm.SimpleRuntimeEnvironment; import jode.jvm.SimpleRuntimeEnvironment;
import jode.jvm.InterpreterException; import jode.jvm.InterpreterException;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.bytecode.BytecodeInfo; import jode.bytecode.BasicBlocks;
import jode.bytecode.TypeSignature; import jode.bytecode.TypeSignature;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -276,9 +276,9 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
MethodIdentifier mi MethodIdentifier mi
= (MethodIdentifier) Main.getClassBundle().getIdentifier(ref); = (MethodIdentifier) Main.getClassBundle().getIdentifier(ref);
if (mi != null) { if (mi != null) {
BytecodeInfo code = mi.info.getBytecode(); BasicBlocks bb = mi.info.getBasicBlocks();
if (code != null) if (bb != null)
return interpreter.interpretMethod(code, cls, params); return interpreter.interpretMethod(bb, cls, params);
} }
throw new InterpreterException("Invoking library method " + ref + "."); throw new InterpreterException("Invoking library method " + ref + ".");
} }

@ -123,6 +123,11 @@ public class FieldIdentifier extends Identifier{
fieldListeners.add(ident); fieldListeners.add(ident);
} }
public void removeFieldListener(Identifier ident) {
if (fieldListeners != null)
fieldListeners.remove(ident);
}
public void setNotConstant() { public void setNotConstant() {
if (notConstant) if (notConstant)
return; return;

@ -52,20 +52,31 @@ public class MethodIdentifier extends Identifier implements Opcodes {
this.clazz = clazz; this.clazz = clazz;
this.info = info; this.info = info;
BytecodeInfo bytecode = info.getBytecode(); BasicBlocks bb = info.getBasicBlocks();
if (bytecode != null) { if (bb != null) {
if ((Main.stripping & Main.STRIP_LVT) != 0) if ((Main.stripping &
info.getBytecode().setLocalVariableTable(null); (Main.STRIP_LVT | Main.STRIP_LNT)) != 0) {
if ((Main.stripping & Main.STRIP_LNT) != 0) Block[] blocks = bb.getBlocks();
info.getBytecode().setLineNumberTable(null); 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(); codeAnalyzer = Main.getClassBundle().getCodeAnalyzer();
CodeTransformer[] trafos CodeTransformer[] trafos
= Main.getClassBundle().getPreTransformers(); = Main.getClassBundle().getPreTransformers();
for (int i = 0; i < trafos.length; i++) { 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]); .reachableClass(exceptions[i]);
} }
BytecodeInfo code = info.getBytecode(); BasicBlocks bb = info.getBasicBlocks();
if (code != null) if (bb != null)
codeAnalyzer.analyzeCode(this, code); codeAnalyzer.analyzeCode(this, bb);
} }
public Identifier getParent() { public Identifier getParent() {
@ -169,63 +180,64 @@ public class MethodIdentifier extends Identifier implements Opcodes {
ClassBundle bundle = Main.getClassBundle(); ClassBundle bundle = Main.getClassBundle();
info.setType(bundle.getTypeAlias(type)); info.setType(bundle.getTypeAlias(type));
if (codeAnalyzer != null) { if (codeAnalyzer != null) {
BytecodeInfo bytecode = info.getBytecode(); BasicBlocks bb = info.getBasicBlocks();
try { try {
codeAnalyzer.transformCode(bytecode); codeAnalyzer.transformCode(bb);
CodeTransformer[] trafos = bundle.getPostTransformers(); CodeTransformer[] trafos = bundle.getPostTransformers();
for (int i = 0; i < trafos.length; i++) { for (int i = 0; i < trafos.length; i++) {
trafos[i].transformCode(bytecode); trafos[i].transformCode(bb);
} }
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
ex.printStackTrace(GlobalOptions.err); ex.printStackTrace(GlobalOptions.err);
bytecode.dumpCode(GlobalOptions.err); bb.dumpCode(GlobalOptions.err);
} }
for (Iterator iter = bytecode.getInstructions().iterator(); Block[] blocks = bb.getBlocks();
iter.hasNext(); ) { for (int i = 0; i < blocks.length; i++) {
Instruction instr = (Instruction) iter.next(); Instruction[] instrs = blocks[i].getInstructions();
switch (instr.getOpcode()) { for (int j = 0; j < instrs.length; j++) {
case opc_invokespecial: switch (instrs[j].getOpcode()) {
case opc_invokestatic: case opc_invokespecial:
case opc_invokeinterface: case opc_invokestatic:
case opc_invokevirtual: { case opc_invokeinterface:
instr.setReference case opc_invokevirtual: {
(Main.getClassBundle() instrs[j].setReference
.getReferenceAlias(instr.getReference())); (Main.getClassBundle()
break; .getReferenceAlias(instrs[j].getReference()));
break;
}
case opc_putstatic: }
case opc_putfield: case opc_putstatic:
case opc_getstatic: case opc_putfield:
case opc_getfield: { case opc_getstatic:
instr.setReference case opc_getfield: {
(Main.getClassBundle() instrs[j].setReference
.getReferenceAlias(instr.getReference())); (Main.getClassBundle()
break; .getReferenceAlias(instrs[j].getReference()));
} break;
case opc_new: }
case opc_checkcast: case opc_new:
case opc_instanceof: case opc_checkcast:
case opc_multianewarray: { case opc_instanceof:
instr.setClazzType case opc_multianewarray: {
(Main.getClassBundle() instrs[j].setClazzType
.getTypeAlias(instr.getClazzType())); (Main.getClassBundle()
break; .getTypeAlias(instrs[j].getClazzType()));
} break;
}
}
} }
} }
Handler[] handlers = bytecode.getExceptionHandlers(); Handler[] handlers = bb.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) { for (int i=0; i< handlers.length; i++) {
if (handlers[i].type != null) { if (handlers[i].getType() != null) {
ClassIdentifier ci = Main.getClassBundle() ClassIdentifier ci = Main.getClassBundle()
.getClassIdentifier(handlers[i].type); .getClassIdentifier(handlers[i].getType());
if (ci != null) if (ci != null)
handlers[i].type = ci.getFullAlias(); handlers[i].setType(ci.getFullAlias());
} }
} }
info.setBytecode(bytecode);
} }
String[] exceptions = info.getExceptions(); String[] exceptions = info.getExceptions();

File diff suppressed because it is too large Load Diff

@ -25,6 +25,8 @@ import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.ListIterator; import java.util.ListIterator;
///#enddef ///#enddef
@ -81,18 +83,14 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
String name; String name;
String type; String type;
Vector usingInstrs = new Vector();
Vector conflictingLocals = new Vector(); Vector conflictingLocals = new Vector();
String id; // for debugging purposes only.
int size; int size;
int newSlot = -1; int newSlot = -1;
LocalInfo() { LocalInfo() {
} }
LocalInfo(InstrInfo instr) {
usingInstrs.addElement(instr);
}
void conflictsWith(LocalInfo l) { void conflictsWith(LocalInfo l) {
if (shadow != null) { if (shadow != null) {
getReal().conflictsWith(l); getReal().conflictsWith(l);
@ -114,82 +112,114 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
if (this == l) if (this == l)
return; return;
shadow = l; shadow = l;
if (shadow.name == null) { if (l.name == null) {
shadow.name = name; l.name = name;
shadow.type = type; l.type = type;
}
Enumeration enum = usingInstrs.elements();
while (enum.hasMoreElements()) {
InstrInfo instr = (InstrInfo) enum.nextElement();
instr.local = l;
l.usingInstrs.addElement(instr);
} }
if (id.compareTo(l.id) < 0)
l.id = id;
} }
public int getFirstAddr() { void generateID(int blockNr, int instrNr) {
int minAddr = Integer.MAX_VALUE; char[] space = new char[5];
Enumeration enum = usingInstrs.elements(); space[0] = (char) ('0' + blockNr / 10);
while (enum.hasMoreElements()) { space[1] = (char) ('0' + blockNr % 10);
InstrInfo info = (InstrInfo) enum.nextElement(); space[2] = (char) ('a' + instrNr / (26*26));
if (info.instr.getAddr() < minAddr) space[3] = (char) ('a' + (instrNr / 26) % 26);
minAddr = info.instr.getAddr(); space[4] = (char) ('a' + instrNr % 26);
} id = new String(space);
return minAddr; }
public String toString() {
return id;
} }
} }
private static class TodoQueue { private static class TodoQueue {
public final InstrInfo LAST = new InstrInfo(); BlockInfo first = null;
InstrInfo first = LAST; BlockInfo last = null;
public void add(InstrInfo info) { public void add(BlockInfo info) {
if (info.nextTodo == null) { if (info.nextTodo == null && info != last) {
/* only enqueue if not already on queue */ /* only enqueue if not already on queue */
info.nextTodo = first; info.nextTodo = first;
first = info; first = info;
if (first == null)
last = info;
} }
} }
public boolean isEmpty() { public boolean isEmpty() {
return first == LAST; return first == null;
} }
public InstrInfo remove() { public BlockInfo remove() {
if (first == LAST) BlockInfo result = first;
throw new NoSuchElementException();
InstrInfo result = first;
first = result.nextTodo; first = result.nextTodo;
result.nextTodo = null; result.nextTodo = null;
if (first == null)
last = null;
return result; return result;
} }
} }
BasicBlocks bb;
TodoQueue changedInfos;
InstrInfo firstInfo;
LocalInfo[] paramLocals;
BlockInfo[] blockInfos;
int maxlocals;
/** /**
* This class contains information for each instruction. * 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. * not changed.
*/ */
InstrInfo nextTodo; BlockInfo nextTodo;
/** /**
* The LocalInfo that this instruction manipulates, or null * The local infos for each Instruction. The index is the
* if this is not an ret, iinc, load or store instruction. * 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 * For each slot, this contains the InstrInfo of one of the
* next Instruction, that may read from that slot, without * next Instruction, that may read from that slot, without
* prior writing. */ * prior writing.
*/
InstrInfo[] nextReads; InstrInfo[] nextReads;
/** /**
* This only has a value for ret instructions. In that case * This only has a value for blocks countaining a ret. In
* this bitset contains all locals, that may be used between * that case this bitset contains all locals, that may be used
* jsr and ret. * between jsr and ret.
*/ */
BitSet usedBySub; BitSet usedBySub;
/** /**
@ -208,26 +238,170 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
* If this instruction is a ret, this contains the single * If this instruction is a ret, this contains the single
* allowed jsr target to which this ret belongs. * allowed jsr target to which this ret belongs.
*/ */
InstrInfo jsrTargetInfo; BlockInfo jsrTargetInfo;
/** /**
* The Instruction of this info * The underlying basic block.
*/ */
Instruction instr; Block block;
/**
* The next info in the chain.
*/ public BlockInfo(int blockNr, Block block) {
InstrInfo nextInfo; 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; case opc_lstore: case opc_dstore:
InstrInfo firstInfo; local.size = 2;
Hashtable instrInfos; /* fall through */
boolean produceLVT; case opc_istore: case opc_fstore: case opc_astore:
int maxlocals; 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() { public LocalOptimizer() {
} }
@ -254,108 +428,41 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
return result; 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() { public void calcLocalInfo() {
maxlocals = bc.getMaxLocals(); maxlocals = bb.getMaxLocals();
Handler[] handlers = bc.getExceptionHandlers(); Block[] blocks = bb.getBlocks();
LocalVariableInfo[] lvt = bc.getLocalVariableTable();
if (lvt != null)
produceLVT = true;
/* Initialize paramLocals */ /* Initialize paramLocals */
{ {
String methodType = bc.getMethodInfo().getType(); String methodType = bb.getMethodInfo().getType();
int paramCount = (bc.getMethodInfo().isStatic() ? 0 : 1) int paramCount = (bb.getMethodInfo().isStatic() ? 0 : 1)
+ TypeSignature.getArgumentSize(methodType); + TypeSignature.getArgumentSize(methodType);
paramLocals = new LocalInfo[paramCount]; paramLocals = new LocalInfo[paramCount];
int slot = 0; int slot = 0;
if (!bc.getMethodInfo().isStatic()) { if (!bb.getMethodInfo().isStatic()) {
LocalInfo local = new LocalInfo(); LocalInfo local = new LocalInfo();
if (lvt != null) { LocalVariableInfo lvi = bb.getParamInfo(slot);
LocalVariableInfo lvi = findLVTEntry(lvt, 0, 0); local.type = "L" + (bb.getMethodInfo().getClazzInfo()
if (lvi != null) { .getName().replace('.', '/'))+";";
local.name = lvi.name; if (local.type.equals(lvi.getType()))
local.type = lvi.type; local.name = lvi.getName();
}
}
local.size = 1; local.size = 1;
local.id = " this";
paramLocals[slot++] = local; paramLocals[slot++] = local;
} }
int pos = 1; int pos = 1;
while (pos < methodType.length() while (pos < methodType.length()
&& methodType.charAt(pos) != ')') { && 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; int start = pos;
pos = TypeSignature.skipType(methodType, pos); pos = TypeSignature.skipType(methodType, pos);
LocalInfo local = new LocalInfo();
LocalVariableInfo lvi = bb.getParamInfo(slot);
local.type = methodType.substring(start, pos); local.type = methodType.substring(start, pos);
if (local.type.equals(lvi.getType()))
local.name = lvi.getName();
local.size = TypeSignature.getTypeSize(local.type); local.size = TypeSignature.getTypeSize(local.type);
local.id = " parm";
paramLocals[slot] = local; paramLocals[slot] = local;
slot += local.size; slot += local.size;
} }
@ -364,97 +471,28 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
/* Initialize the InstrInfos and LocalInfos /* Initialize the InstrInfos and LocalInfos
*/ */
changedInfos = new TodoQueue(); changedInfos = new TodoQueue();
instrInfos = new Hashtable(); blockInfos = new BlockInfo[blocks.length];
{ for (int i=0; i< blocks.length; i++)
InstrInfo info = firstInfo = new InstrInfo(); blockInfos[i] = new BlockInfo(i, blocks[i]);
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;
case opc_ret: for (int i=0; i< blocks.length; i++) {
/* this is a ret instruction */ int[] succs = blocks[i].getSuccs();
info.usedBySub = new BitSet(); for (int j=0; j< succs.length; j++) {
info.nextReads[instr.getLocalSlot()] = info; if (succs[j] >= 0)
changedInfos.add(info); blockInfos[succs[j]].preds.add(blockInfos[i]);
break; }
BasicBlocks.Handler[] handlers = blocks[i].getCatcher();
case opc_lstore: case opc_dstore: for (int j=0; j< handlers.length; j++) {
info.local.size = 2; blockInfos[handlers[j].getCatcher()]
//case opc_istore: case opc_fstore: case opc_astore: .tryBlocks.add(blockInfos[i]);
}
}
if (!i.hasNext())
break;
info = info.nextInfo = new InstrInfo();
} }
} }
/* find out which locals are the same. /* find out which locals are the same.
*/ */
while (!changedInfos.isEmpty()) { while (!changedInfos.isEmpty()) {
InstrInfo info = changedInfos.remove(); BlockInfo info = changedInfos.remove();
Instruction instr = info.instr; info.promoteIns();
/* 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);
}
}
}
if (instr.getPreds() != null) { if (instr.getPreds() != null) {
for (int i = 0; i < instr.getPreds().length; i++) { for (int i = 0; i < instr.getPreds().length; i++) {
@ -500,48 +538,45 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
promoteReads(info, instr.getPreds()[i]); 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; changedInfos = null;
/* Now merge with the parameters /* Now merge with the parameters
* The params should be the locals in firstInfo.nextReads * The params should be the locals in firstInfo.nextReads
*/ */
for (int i=0; i< paramLocals.length; i++) { int startBlock = bb.getStartBlock();
if (firstInfo.nextReads[i] != null) { if (startBlock >= 0) {
firstInfo.nextReads[i].local.combineInto(paramLocals[i]); LocalInfo[] ins = blockInfos[startBlock].ins;
paramLocals[i] = paramLocals[i].getReal(); for (int i=0; i< paramLocals.length; i++) {
if (ins[i] != null)
paramLocals[i].combineInto(ins[i]);
} }
} }
} }
public void stripLocals() { public void stripLocals() {
ListIterator iter = bc.getInstructions().listIterator(); Block[] blocks = bb.getBlocks();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { for (int i = 0; i < blocks.length; i++) {
Instruction instr = (Instruction) iter.next(); Instruction[] instrs = blocks[i].getInstructions();
if (info.local != null && info.local.usingInstrs.size() == 1) { for (int j = 0; j < instrs.length; j++) {
/* If this is a store, whose value is never read; it can Instruction instr = instrs[j];
* be removed, i.e replaced by a pop. */ if (info.local != null
switch (instr.getOpcode()) { && info.local.usingBlocks.size() == 1) {
case opc_istore: /* If this is a store, whose value is never read; it can
case opc_fstore: * be removed, i.e replaced by a pop.
case opc_astore: */
iter.set(new Instruction(opc_pop)); switch (instr.getOpcode()) {
break; case opc_istore:
case opc_lstore: case opc_fstore:
case opc_dstore: case opc_astore:
iter.set(new Instruction(opc_pop2)); instrs[j] = Instruction.forOpcode(opc_pop);
break; break;
default: 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. /* Now calculate the conflict settings.
*/ */
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { for (int blockNr = 0; blockNr < blockInfos.length; blockNr++) {
if (info.instr.getOpcode() >= BytecodeInfo.opc_istore blockInfos[blockNr].generateConflicts();
&& 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);
}
}
}
}
}
} }
/* Now put the locals that need a color into a vector. /* Now put the locals that need a color into a vector.
@ -670,11 +675,6 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
} }
} }
bc.setMaxLocals(maxlocals); bc.setMaxLocals(maxlocals);
/* Update LocalVariableTable
*/
if (produceLVT)
buildNewLVT();
} }
private InstrInfo CONFLICT = new InstrInfo(); private InstrInfo CONFLICT = new InstrInfo();
@ -704,222 +704,63 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
return changed; 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() { public void dumpLocals() {
Vector locals = new Vector(); Vector locals = new Vector();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { for (int blockNr=0; blockNr < blockInfos.length; blockNr++) {
GlobalOptions.err.println(info.instr.getDescription()); BlockInfo info = blockInfos[blockNr];
GlobalOptions.err.print("nextReads: "); GlobalOptions.err.print("ins: [");
for (int i=0; i<maxlocals; i++) for (int i=0; i<maxlocals; i++) {
if (info.nextReads[i] == null) if (info.ins[i] == null)
GlobalOptions.err.print("-,"); GlobalOptions.err.print("-----,");
else
GlobalOptions.err.print(info.ins[i].toString()+",");
}
GlobalOptions.err.println("]");
blockInfos.block.dumpCode(GlobalOptions.err);
GlobalOptions.err.print("gens: [");
for (int i=0; i<maxlocals; i++) {
if (info.ins[i] == null)
GlobalOptions.err.print("-----,");
else else
GlobalOptions.err.print(info.nextReads[i].instr.getAddr()+","); GlobalOptions.err.print(info.ins[i].toString()+",");
}
GlobalOptions.err.println("]");
if (info.usedBySub != null) if (info.usedBySub != null)
GlobalOptions.err.print(" usedBySub: "+info.usedBySub); GlobalOptions.err.println(" usedBySub: "+info.usedBySub);
if (info.retInfo != null) if (info.retInfo != null)
GlobalOptions.err.print(" ret info: " GlobalOptions.err.println(" ret info: "
+info.retInfo.instr.getAddr()); +info.retInfo.instr.getAddr());
if (info.jsrTargetInfo != null) if (info.jsrTargetInfo != null)
GlobalOptions.err.print(" jsr info: " GlobalOptions.err.println(" jsr info: "
+info.jsrTargetInfo.instr.getAddr()); +info.jsrTargetInfo.instr.getAddr());
GlobalOptions.err.println();
if (info.local != null && !locals.contains(info.local)) if (info.local != null && !locals.contains(info.local))
locals.addElement(info.local); locals.addElement(info.local);
} }
Enumeration enum = locals.elements(); // Enumeration enum = locals.elements();
while (enum.hasMoreElements()) { // while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo) enum.nextElement(); // LocalInfo li = (LocalInfo) enum.nextElement();
int slot = ((InstrInfo)li.usingInstrs.elementAt(0)) // int slot = ((InstrInfo)li.usingInstrs.elementAt(0))
.instr.getLocalSlot(); // .instr.getLocalSlot();
GlobalOptions.err.print("Slot: "+slot+" conflicts:"); // GlobalOptions.err.print("Slot: "+slot+" conflicts:");
Enumeration enum1 = li.conflictingLocals.elements(); // Enumeration enum1 = li.conflictingLocals.elements();
while (enum1.hasMoreElements()) { // while (enum1.hasMoreElements()) {
LocalInfo cfl = (LocalInfo)enum1.nextElement(); // LocalInfo cfl = (LocalInfo)enum1.nextElement();
GlobalOptions.err.print(cfl.getFirstAddr()+", "); // GlobalOptions.err.print(cfl.getAddr()+", ");
} // }
GlobalOptions.err.println(); // GlobalOptions.err.println();
GlobalOptions.err.print(li.getFirstAddr()); // GlobalOptions.err.print(li.getAddr());
GlobalOptions.err.print(" instrs: "); // GlobalOptions.err.print(" instrs: ");
Enumeration enum2 = li.usingInstrs.elements(); // Enumeration enum2 = li.usingInstrs.elements();
while (enum2.hasMoreElements()) // while (enum2.hasMoreElements())
GlobalOptions.err.print(((InstrInfo)enum2.nextElement()) // GlobalOptions.err.print(((InstrInfo)enum2.nextElement())
.instr.getAddr()+", "); // .instr.getAddr()+", ");
GlobalOptions.err.println(); // GlobalOptions.err.println();
} // }
GlobalOptions.err.println("-----------"); // GlobalOptions.err.println("-----------");
} }
public void transformCode(BytecodeInfo bytecode) { public void transformCode(BasicBlocks bb) {
this.bc = bytecode; this.bb = bb;
calcLocalInfo(); calcLocalInfo();
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_LOCALS) != 0) { & GlobalOptions.DEBUG_LOCALS) != 0) {

@ -10,6 +10,7 @@ FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):
MY_JAVA_FILES = \ MY_JAVA_FILES = \
ConstantAnalyzer.java \ ConstantAnalyzer.java \
IdentityRenamer.java \
KeywordRenamer.java \ KeywordRenamer.java \
LocalOptimizer.java \ LocalOptimizer.java \
ModifierMatcher.java \ ModifierMatcher.java \
@ -21,7 +22,6 @@ MY_JAVA_FILES = \
StrongRenamer.java \ StrongRenamer.java \
UniqueRenamer.java \ UniqueRenamer.java \
WildCard.java WildCard.java
# LocalizeFieldTransformer.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class) noinst_DATA = $(MY_JAVA_FILES:.java=.class)
EXTRA_DIST = $(MY_JAVA_FILES) EXTRA_DIST = $(MY_JAVA_FILES)

@ -23,288 +23,568 @@ import jode.obfuscator.*;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import java.util.BitSet;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ListIterator; import java.util.ListIterator;
import java.util.LinkedList;
import java.util.Stack;
///#enddef ///#enddef
public class RemovePopAnalyzer implements CodeTransformer, Opcodes { public class RemovePopAnalyzer implements CodeTransformer, Opcodes {
public RemovePopAnalyzer() { public RemovePopAnalyzer() {
} }
public void transformCode(BytecodeInfo bytecode) { static class BlockInfo {
/* A bitset of stack entries at the beginning of the block,
* whose values should be never put put onto the stack.
* This array is shared with all other blocks that have
* a common predecessor.
*/
int[] poppedBefore;
/* A bitset of instructions, that should be removed, i.e. their
* parameters should just get popped.
*/
int[] removedInstrs;
ArrayList predecessors;
BlockInfo(int[] popped, int[] removed) {
this.poppedEntries = popped;
this.removedInstrs = removed;
}
boolean isPopped(int pos) {
return (poppedEntries[pos >> 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]; int poppush[] = new int[2];
ListIterator iter = bytecode.getInstructions().listIterator(); int maxStack = bb.getMaxStack();
next_pop: int poppedLen = maxStack >> 5;
while (iter.hasNext()) {
Instruction popInstr = (Instruction) iter.next();
boolean isPop2 = false;
switch (popInstr.getOpcode()) {
case opc_nop: {
iter.remove();
continue;
}
case opc_pop2: for (int i = 0; i < blocks.length; i++) {
isPop2 = true; BitSet popped = new BitSet();
case opc_pop: Instruction[] instrs = blocks[i].getInstructions();
if (popInstr.getPreds() != null) int[] removed = instrs.length >> 5;
// 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);
if (count < poppush[1]) { // Be conservative with stack depth at end of block
if (count == 0) int depth = maxStack;
break;
int opcode = instr.getOpcode(); for (int j = instrs.length; j-- > 0; ) {
/* If this is a dup and the instruction popped is the Instruction instr = instrs[j];
* 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 instr.getStackPopPush(poppush);
&& count > 1 && count <= 4
&& opcode == (opc_dup2 + count-2)) { // Now check if the parameters of this instr are needed.
iter.remove(); boolean params_needed = false;
continue next_pop; switch (instr.getOpcode()) {
} case opc_invokevirtual:
/* Otherwise popping is not possible */ case opc_invokespecial:
instr = null; case opc_invokestatic:
break; 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) { if (params_needed) {
// We insert the pop at the previous position /* mark params as needed */
while (iter.next() != popPrevious) for (int k = 0; k < poppush[0]; k++)
{} popped.clear(--depth);
if (!isPop2 && popPrevious.getOpcode() == opc_pop) { } else {
// merge pop with popPrevious removed[j >> 5] |= 1 << (j & 31);
iter.set(new Instruction(opc_pop2)); /* mark params as popped */
} else for (int k = 0; k < poppush[0]; k++)
iter.add(popInstr); popped.set(--depth);
continue;
} }
int opcode = instr.getOpcode(); }
switch (opcode) {
case opc_ldc2_w: int[] poppedArr = new int[poppedLen];
case opc_lload: case opc_dload: if (blocks[i] != bb.getStartBlock()) {
if (!isPop2) /* Only can pop entries before this block if
throw new AssertError("pop on long"); * this isn't the start block.
iter.remove(); */
continue; for (int k = 0; k < block_pop; k++) {
case opc_ldc: if (popped.get(depth+k))
case opc_iload: case opc_fload: case opc_aload: poppedArr[k >> 5] |= 1 << (k & 31);
case opc_dup: }
case opc_new: }
if (isPop2) infos[i] = new BlockInfo(poppedArr, removed);
iter.set(new Instruction(opc_pop)); }
else
iter.remove();
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));
continue;
case opc_dup_x1: /* Now start sharing poppedEntries as necessary. */
iter.set(new Instruction(opc_swap)); int[] empty = new int[poppedLen];
iter.next(); next_block:
if (isPop2) for (int i = 1; i < blocks.length; i++) {
iter.add(new Instruction(opc_pop)); /* 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; continue;
case opc_dup2: int blockNr = -1;
if (isPop2) { boolean isAPred = false;
iter.remove(); for (int k = 0; k < succs.length; k++) {
continue; 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;
} }
break; }
case opc_swap: }
if (isPop2) { }
iter.set(popInstr);
continue; /* 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;
} }
break; }
if (succPops == null)
continue;
blocks[i].getStackPopPush(poppush);
int[] myPops = infos[i].poppedPush;
for (int k = poppush[1]; k < maxStack; k++) {
case opc_lneg: case opc_dneg: if (succs.length == 0 || succs[0] == null)
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; 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;
for (int k = 0; k < poppedLen; k++) {
succsPoppedEntries &=
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_l2i: case opc_l2f: case opc_pop:
case opc_d2i: case opc_d2f: case opc_pop2:
if (isPop2) { break;
iter.next();
iter.add(new Instruction(opc_pop)); default:
iter.previous(); /* Check if all results are needed, adjust depth */
iter.previous(); for (int k = 0; k < poppush[1]; k++) {
if (!popped.get(depth++))
params_needed = true;
} }
iter.set(new Instruction(opc_pop2)); /* If opcode has no result it has side effects. */
continue; if (poppush[1] == 0)
params_needed = true;
}
if (block_pop < maxStack - depth)
block_pop = maxStack - depth;
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_ladd: case opc_dadd: if (blocks[j] == null)
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: return infos;
if (!isPop2) }
throw new AssertError("pop on long");
iter.next(); public void transformCode(BasicBlocks bb) {
iter.add(popInstr); if (bb.getStartBlock() == null)
iter.previous(); return;
iter.previous();
iter.set(new Instruction(opc_pop2)); 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; continue;
case opc_lshl: case opc_lshr: case opc_lushr: if (instr.getOpcode() == opc_pop) {
if (!isPop2) poppedEntries[stackDepth++] = true;
throw new AssertError("pop on long");
iter.next();
iter.add(popInstr);
iter.previous();
iter.previous();
iter.set(new Instruction(opc_pop));
continue; continue;
}
case opc_i2l: case opc_i2d: if (instr.getOpcode() == opc_pop2) {
case opc_f2l: case opc_f2d: poppedEntries[stackDepth++] = true;
if (!isPop2) poppedEntries[stackDepth++] = true;
throw new AssertError("pop on long");
iter.set(new Instruction(opc_pop));
continue; continue;
}
case opc_lcmp: instr.getStackPopPush(poppush);
case opc_dcmpl: case opc_dcmpg: // First look if stackDepth was right
iter.next(); if (stackDepth < poppush[1]) {
iter.add(new Instruction(opc_pop2)); int diff = poppush[1] - stackDepth;
if (isPop2) { System.arraycopy(poppedEntries, 0,
iter.add(new Instruction(opc_pop)); poppedEntries, diff, stackDepth);
iter.previous(); for (int j=0; j< diff; i++)
} poppedEntries[j] = false;
iter.previous(); }
iter.previous(); // Now check if this instr pushes a popped Entry.
iter.set(new Instruction(opc_pop2)); boolean push_a_popped = false;
continue; 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_getstatic: int count = (opcode - opc_dup)/3+1;
case opc_getfield: { int depth = (opcode - opc_dup)%3;
Reference ref = instr.getReference(); stackDepth -= count;
int size = TypeSignature.getTypeSize(ref.getType()); int bottom = stackDepth - count - depth;
if (size == 2 && !isPop2)
throw new AssertError("pop on long"); int popped1 = 0;
if (opcode == opc_getfield) int popped3 = 0;
size--; int newDepth = 0;
switch (size) {
case 0: // get the popped mask and adjust poppedEntries.
iter.set(popInstr); for (int j=0; j< count; j++) {
break; if (poppedEntries[bottom + j])
case 1: popped1 |= 1<<j;
if (isPop2) { if (poppedEntries[bottom + count + depth + j])
iter.set(new Instruction(opc_pop)); popped3 |= 1<<j;
}
for (int j=0; j< depth; j++) {
if (poppedEntries[bottom + count + j])
newDepth++;
poppedEntries[bottom + j]
= poppedEntries[bottom + count + j];
}
for (int j=0; j< count; j++) {
poppedEntries[bottom + depth + j]
= (popped1 & popped3 & (1 << j)) != 0;
}
// adjust the depth
depth = newDepth;
int all = (count == 2) ? 3 : 1;
if (popped1 == all)
// dup was not necessary
break; break;
if (depth == 0 && popped3 == all)
// dup was not necessary
break;
// adjust the count
if ((popped1 & popped3 & 1) != 0) {
count--;
popped1 >>= 1;
popped3 >>= 2;
} else if ((popped1 & popped3 & 2) != 0) {
count--;
popped1 &= 1;
popped3 &= 1;
} }
/* fall through */
case 2:
iter.remove();
}
continue;
}
case opc_multianewarray: { if (popped1 == 1) {
int dims = instr.getDimensions(); // count == 2, popped1 = 1,
if (--dims > 0) { depth++;
iter.next(); count--;
while (dims-- > 0) { popped1 = 0;
iter.add(new Instruction(opc_pop)); popped3 >>= 1;
iter.previous(); }
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;
}
} }
iter.previous();
}
iter.set(popInstr);
continue;
}
case opc_invokevirtual: if (count == 1) {
case opc_invokespecial: // Possible states:
case opc_invokestatic: // popped1 = 0; popped3 = 1;
case opc_invokeinterface: // depth = 1 or depth = 2
if (TypeSignature.getReturnSize if (depth == 1) {
(instr.getReference().getType()) != 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; break;
/* fall through */ }
case opc_checkcast: case opc_swap: {
if (isPop2) { // swap the popped status
/* This is/may be a double pop on a single value boolean tmp = poppedEntries[stackDepth - 1];
* split it and continue with second half poppedEntries[stackDepth - 1]
*/ = poppedEntries[stackDepth - 2];
iter.next(); poppedEntries[stackDepth - 2] = tmp;
iter.add(new Instruction(opc_pop)); }
iter.add(new Instruction(opc_pop));
iter.previous();
// 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; 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());
} }
} }
} }

@ -18,10 +18,11 @@
*/ */
package jode.obfuscator.modules; package jode.obfuscator.modules;
import jode.bytecode.Handler;
import jode.bytecode.Opcodes; import jode.bytecode.Opcodes;
import jode.bytecode.ClassInfo; 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.Instruction;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.bytecode.TypeSignature; import jode.bytecode.TypeSignature;
@ -30,6 +31,7 @@ import jode.obfuscator.*;
import jode.GlobalOptions; import jode.GlobalOptions;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.ListIterator; import java.util.ListIterator;
///#enddef ///#enddef
@ -93,94 +95,108 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
* references * references
* @return an enumeration of the references. * @return an enumeration of the references.
*/ */
public void analyzeCode(MethodIdentifier m, BytecodeInfo bytecode) { public void analyzeCode(MethodIdentifier m, BasicBlocks bb) {
for (Iterator iter = bytecode.getInstructions().iterator(); Block[] blocks = bb.getBlocks();
iter.hasNext(); ) { for (int i=0; i < blocks.length; i++) {
Instruction instr = (Instruction) iter.next(); Instruction[] instrs = blocks[i].getInstructions();
switch (instr.getOpcode()) { for (int idx = 0; idx < instrs.length; idx++) {
case opc_checkcast: int opcode = instrs[idx].getOpcode();
case opc_instanceof: switch (opcode) {
case opc_multianewarray: { case opc_checkcast:
String clName = instr.getClazzType(); case opc_instanceof:
int i = 0; case opc_multianewarray: {
while (i < clName.length() && clName.charAt(i) == '[') String clName = instrs[idx].getClazzType();
i++; int k = 0;
if (i < clName.length() && clName.charAt(i) == 'L') { while (k < clName.length() && clName.charAt(k) == '[')
clName = clName.substring(i+1, clName.length()-1) k++;
.replace('/','.'); if (k < clName.length() && clName.charAt(k) == 'L') {
Main.getClassBundle().reachableClass(clName); clName = clName.substring(k+1, clName.length()-1)
.replace('/','.');
Main.getClassBundle().reachableClass(clName);
}
break;
} }
break; case opc_invokespecial:
} case opc_invokestatic:
case opc_invokespecial: case opc_invokeinterface:
case opc_invokestatic: case opc_invokevirtual:
case opc_invokeinterface: case opc_putstatic:
case opc_invokevirtual: case opc_putfield:
case opc_putstatic: m.setGlobalSideEffects();
case opc_putfield: /* fall through */
m.setGlobalSideEffects(); case opc_getstatic:
/* fall through */ case opc_getfield: {
case opc_getstatic: Identifier ident = canonizeReference(instrs[idx]);
case opc_getfield: { if (ident != null) {
Identifier ident = canonizeReference(instr); if (opcode == opc_putstatic
if (ident != null) { || opcode == opc_putfield) {
if (instr.getOpcode() == opc_putstatic FieldIdentifier fi = (FieldIdentifier) ident;
|| instr.getOpcode() == opc_putfield) { if (!fi.isNotConstant())
FieldIdentifier fi = (FieldIdentifier) ident; fi.setNotConstant();
if (!fi.isNotConstant()) } else if (opcode == opc_invokevirtual ||
fi.setNotConstant(); opcode == opc_invokeinterface) {
} else if (instr.getOpcode() == opc_invokevirtual ((ClassIdentifier) ident.getParent())
|| instr.getOpcode() == opc_invokeinterface) { .reachableReference
((ClassIdentifier) ident.getParent()) (instrs[idx].getReference(), true);
.reachableReference(instr.getReference(), true); } else {
} else { ident.setReachable();
ident.setReachable(); }
} }
break;
}
} }
break;
}
} }
} }
Handler[] handlers = bytecode.getExceptionHandlers(); Handler[] handlers = bb.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) { for (int i=0; i< handlers.length; i++) {
if (handlers[i].type != null) if (handlers[i].getType() != null)
Main.getClassBundle() Main.getClassBundle()
.reachableClass(handlers[i].type); .reachableClass(handlers[i].getType());
} }
} }
public void transformCode(BytecodeInfo bytecode) { public void transformCode(BasicBlocks bb) {
for (ListIterator iter = bytecode.getInstructions().listIterator(); Block[] blocks = bb.getBlocks();
iter.hasNext(); ) { for (int i=0; i < blocks.length; i++) {
Instruction instr = (Instruction) iter.next(); Instruction[] instrs = blocks[i].getInstructions();
if (instr.getOpcode() == opc_putstatic ArrayList newCode = new ArrayList();
|| instr.getOpcode() == opc_putfield) { Block[] newSuccs = blocks[i].getSuccs();
Reference ref = instr.getReference(); for (int idx = 0; idx < instrs.length; idx++) {
FieldIdentifier fi = (FieldIdentifier) int opcode = instrs[idx].getOpcode();
Main.getClassBundle().getIdentifier(ref); if (opcode == opc_putstatic || opcode == opc_putfield) {
if (fi != null Reference ref = instrs[idx].getReference();
&& (Main.stripping & Main.STRIP_UNREACH) != 0 FieldIdentifier fi = (FieldIdentifier)
&& !fi.isReachable()) { Main.getClassBundle().getIdentifier(ref);
/* Replace instruction with pop opcodes. */ if (fi != null
int stacksize = && (Main.stripping & Main.STRIP_UNREACH) != 0
(instr.getOpcode() && !fi.isReachable()) {
== Instruction.opc_putstatic) ? 0 : 1; /* Replace instruction with pop opcodes. */
stacksize += TypeSignature.getTypeSize(ref.getType()); int stacksize =
switch (stacksize) { (opcode == Instruction.opc_putstatic) ? 0 : 1;
case 1: stacksize += TypeSignature.getTypeSize(ref.getType());
iter.set(new Instruction(Instruction.opc_pop)); switch (stacksize) {
break; case 1:
case 2: newCode.add(Instruction.forOpcode
iter.set(new Instruction(Instruction.opc_pop2)); (Instruction.opc_pop));
break; continue;
case 3: case 2:
iter.set(new Instruction(Instruction.opc_pop2)); newCode.add(Instruction.forOpcode
iter.add(new Instruction(Instruction.opc_pop)); (Instruction.opc_pop2));
break; 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);
} }
} }
} }

@ -27,22 +27,38 @@ import java.io.IOException;
* *
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
public class ArrayType extends ReferenceType { public class ArrayType extends ClassType {
// 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")
};
Type elementType; Type elementType;
ArrayType(Type elementType) { ArrayType(Type elementType) {
super(TC_ARRAY); super(TC_ARRAY, elementType + "[]");
this.elementType = 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() { public Type getElementType() {
return elementType; return elementType;
} }
@ -64,6 +80,14 @@ public class ArrayType extends ReferenceType {
return tArray(elementType.getCanonic()); 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. * Create the type corresponding to the range from bottomType to this.
* @param bottomType the start point of the range * @param bottomType the start point of the range
@ -75,22 +99,11 @@ public class ArrayType extends ReferenceType {
* obj , tArray(x) -> <obj, tArray(x)> * obj , tArray(x) -> <obj, tArray(x)>
* iff tArray extends and implements obj * iff tArray extends and implements obj
*/ */
if (bottom.getTypeCode() == TC_ARRAY) if (bottom instanceof ArrayType)
return tArray(elementType.intersection return tArray(elementType.intersection
(((ArrayType)bottom).elementType)); (((ArrayType)bottom).elementType));
if (bottom.getTypeCode() == TC_CLASS) { return super.createRangeType(bottom);
ClassInterfacesType bottomCIT = (ClassInterfacesType)bottom;
// int len = arrayIfacesStrs.length;
// ClassInfo[] arrayIfaces = new ClassInfo[len];
// for (int i=0; i< arrayIfacesStrs.length; i++)
// arrayIfaces[i]
// = bottomCIT.classPath.getClassInfo(arrayIfacesStrs[i]);
if (bottomCIT.clazz == null
&& implementsAllIfaces(null, arrayIfaces, bottomCIT.ifaces))
return tRange(bottomCIT, this);
}
return tError;
} }
/** /**
@ -100,9 +113,8 @@ public class ArrayType extends ReferenceType {
*/ */
public Type getSpecializedType(Type type) { public Type getSpecializedType(Type type) {
/* /*
* tArray(x), object -> tArray(x) iff tArray implements object
* tArray(x), tArray(y) -> tArray(x.intersection(y)) * 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) { if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getBottom(); type = ((RangeType) type).getBottom();
@ -113,12 +125,8 @@ public class ArrayType extends ReferenceType {
return tArray(elementType.intersection return tArray(elementType.intersection
(((ArrayType)type).elementType)); (((ArrayType)type).elementType));
} }
if (type.getTypeCode() == TC_CLASS) { if (type.isSubTypeOf(this))
ClassInterfacesType other = (ClassInterfacesType) type; return this;
if (other.clazz == null
&& implementsAllIfaces(null, arrayIfaces, other.ifaces))
return this;
}
return tError; return tError;
} }
@ -138,78 +146,14 @@ public class ArrayType extends ReferenceType {
} }
if (type == tNull) if (type == tNull)
return this; return this;
if (type.getTypeCode() == TC_ARRAY) if (type instanceof ArrayType)
return tArray(elementType.intersection return tArray(elementType.intersection
(((ArrayType)type).elementType)); (((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 if (!(type instanceof ReferenceType))
* implemented by one interface or class in each group. return tError;
*
* 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<other.ifaces.length; j++) {
if (arrayIfaces[i].implementedBy(other.ifaces[j])) {
newIfaces.addElement(arrayIfaces[i]);
continue iface_loop;
}
}
}
ClassInfo[] ifaceArray = new ClassInfo[newIfaces.size()];
newIfaces.copyInto(ifaceArray);
return ClassInterfacesType.create(null, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
}
}
return tError;
}
/** return ((ReferenceType)type).getGeneralizedType(this);
* 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 (!elementType.isClassType()
|| !((ArrayType)hintType).elementType.isClassType())
return tObject;
Type middleType = elementType.getCastHelper
(((ArrayType)hintType).elementType);
if (middleType != null)
return tArray(middleType);
return null;
case TC_CLASS:
ClassInterfacesType hint = (ClassInterfacesType) hintType;
if (hint.clazz == null
&& implementsAllIfaces(null, arrayIfaces, hint.ifaces))
return null;
return tObject;
case TC_UNKNOWN:
return null;
}
return tObject;
} }
/** /**
@ -220,20 +164,12 @@ public class ArrayType extends ReferenceType {
return elementType.isValidType(); return elementType.isValidType();
} }
public boolean isClassType() {
return true;
}
public String getTypeSignature() { public String getTypeSignature() {
return "["+elementType.getTypeSignature(); return "["+elementType.getTypeSignature();
} }
public Class getTypeClass() throws ClassNotFoundException { public Class getTypeClass() throws ClassNotFoundException {
return Class.forName("["+elementType.getTypeSignature()); return Class.forName(getTypeSignature());
}
public String toString() {
return elementType.toString()+"[]";
} }
private static String pluralize(String singular) { private static String pluralize(String singular) {

@ -0,0 +1,108 @@
/* ClassInfoType 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 jode.GlobalOptions;
import java.lang.reflect.Modifier;
import java.io.IOException;
import java.util.Vector;
import java.util.Stack;
import java.util.Hashtable;
/**
* This class is the type representing a class loaded from a ClassPath.<p>
*
* @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);
}
}

@ -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.<p>
*
* 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<other.ifaces.length; j++) {
if (iface.implementedBy(other.ifaces[j])) {
continue big_loop_this;
}
}
/* This interface is not implemented by any of the other
* ifaces. Add it to the interfaces vector.
*/
ifaces.addElement(iface);
}
big_loop_other:
for (int i=0; i< other.ifaces.length; i++) {
ClassInfo iface = other.ifaces[i];
if (clazz != null && iface.implementedBy(clazz)) {
continue big_loop_other;
}
for (int j=0; j<ifaces.size(); j++) {
if (iface.implementedBy((ClassInfo)
ifaces.elementAt(j))) {
continue big_loop_other;
}
}
/* This interface is not implemented by any of the other
* ifaces. Add it to the interfaces vector.
*/
ifaces.addElement(iface);
}
ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()];
ifaces.copyInto(ifaceArray);
return create(clazz, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
}
/**
* 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) {
try{
int code = type.typecode;
if (code == TC_RANGE) {
type = ((RangeType) type).getTop();
code = type.typecode;
}
if (code == TC_NULL)
return this;
if (code == TC_ARRAY)
return ((ArrayType) type).getGeneralizedType(this);
if (code != TC_CLASS)
return tError;
ClassInterfacesType other = (ClassInterfacesType) type;
ClassInfo clazz;
/* First the easy part, determine the clazz */
if (this.clazz == null || other.clazz == null)
clazz = null;
else {
clazz = this.clazz;
try {
while(clazz != null) {
if (clazz.superClassOf(other.clazz))
break;
clazz = clazz.getSuperclass();
}
if (clazz.getName() == "java.lang.Object")
clazz = null;
} catch (IOException ex) {
/*XXX : There is something better than tObject*/
clazz = null;
}
}
if (clazz == this.clazz
&& implementsAllIfaces(other.clazz, other.ifaces, this.ifaces))
return this;
else if (clazz == other.clazz
&& implementsAllIfaces(this.clazz, this.ifaces, 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.
*/
Stack allIfaces = new Stack();
if (this.clazz != null) {
ClassInfo c = this.clazz;
while (clazz != c) {
ClassInfo clazzIfaces[] = c.getInterfaces();
for (int i=0; i<clazzIfaces.length; i++)
allIfaces.push(clazzIfaces[i]);
c = c.getSuperclass();
}
}
Vector ifaces = new Vector();
for (int i=0; i<this.ifaces.length; i++)
allIfaces.push(this.ifaces[i]);
/* Now consider each interface. If any clazz or interface
* in other implements it, add it to the ifaces vector.
* Otherwise consider all sub interfaces.
*/
iface_loop:
while (!allIfaces.isEmpty()) {
ClassInfo iface = (ClassInfo) allIfaces.pop();
if ((clazz != null && iface.implementedBy(clazz))
|| ifaces.contains(iface))
/* We can skip this, as clazz or ifaces already imply it.
*/
continue iface_loop;
if (other.clazz != null && iface.implementedBy(other.clazz)) {
ifaces.addElement(iface);
continue iface_loop;
}
for (int i=0; i<other.ifaces.length; i++) {
if (iface.implementedBy(other.ifaces[i])) {
ifaces.addElement(iface);
continue iface_loop;
}
}
/* This interface is not implemented by any of the other
* ifaces. Try its parent interfaces now.
*/
ClassInfo clazzIfaces[] = iface.getInterfaces();
for (int i=0; i<clazzIfaces.length; i++)
allIfaces.push(clazzIfaces[i]);
}
ClassInfo[] ifaceArray = new ClassInfo[ifaces.size()];
ifaces.copyInto(ifaceArray);
return create(clazz, ifaceArray);
} catch (IOException ex) {
/*XXX : There is something better than tError*/
return tError;
}
}
public String getTypeSignature() {
if (clazz != null)
return "L" + clazz.getName().replace('.','/') + ";";
else if (ifaces.length > 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<ifaces.length; j++) {
if (type.ifaces[i] == ifaces[j])
continue big_loop;
}
return false;
}
return true;
}
}
return false;
}
}

@ -0,0 +1,332 @@
/* ClassType Copyright (C) 2000 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 java.util.Stack;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.IOException;
/**
* This class is the base class of all types representing a class type.<p>
*
* @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;
}
}

@ -138,6 +138,16 @@ public class IntegerType extends Type {
return true; 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 &lt;unknown -- type&rt; are not disjunct. * Check if this and &lt;unknown -- type&rt; are not disjunct.
* @param type a simple type; this mustn't be a range type. * @param type a simple type; this mustn't be a range type.

@ -10,12 +10,15 @@ FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):
MY_JAVA_FILES = \ MY_JAVA_FILES = \
ArrayType.java \ ArrayType.java \
ClassInterfacesType.java \ ClassType.java \
ClassInfoType.java \
IntegerType.java \ IntegerType.java \
MethodType.java \ MethodType.java \
MultiClassType.java \
NullType.java \ NullType.java \
RangeType.java \ RangeType.java \
ReferenceType.java \ ReferenceType.java \
SystemClassType.java \
Type.java Type.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class) noinst_DATA = $(MY_JAVA_FILES:.java=.class)

@ -18,6 +18,7 @@
*/ */
package jode.type; package jode.type;
import jode.bytecode.ClassPath;
/** /**
* This type represents an method type. * This type represents an method type.
@ -28,9 +29,11 @@ public class MethodType extends Type {
final String signature; final String signature;
final Type[] parameterTypes; final Type[] parameterTypes;
final Type returnType; final Type returnType;
final ClassPath cp;
public MethodType(String signature) { public MethodType(ClassPath cp, String signature) {
super(TC_METHOD); super(TC_METHOD);
this.cp = cp;
this.signature = signature; this.signature = signature;
int index = 1, types = 0; int index = 1, types = 0;
while (signature.charAt(index) != ')') { while (signature.charAt(index) != ')') {
@ -53,9 +56,9 @@ public class MethodType extends Type {
index = signature.indexOf(';', index); index = signature.indexOf(';', index);
index++; index++;
parameterTypes[types++] 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() { public final int stackSize() {
@ -65,6 +68,10 @@ public class MethodType extends Type {
return size; return size;
} }
public ClassPath getClassPath() {
return cp;
}
public Type[] getParameterTypes() { public Type[] getParameterTypes() {
return parameterTypes; return parameterTypes;
} }
@ -91,10 +98,4 @@ public class MethodType extends Type {
public String toString() { public String toString() {
return signature; return signature;
} }
public boolean equals(Object o) {
MethodType mt;
return (o instanceof MethodType
&& signature.equals((mt = (MethodType)o).signature));
}
} }

@ -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.<p>
*
* 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<classes.length; j++) {
if (type.classes[i] == classes[j])
continue big_loop;
}
return false;
}
return true;
}
}
return false;
}
}

@ -19,6 +19,10 @@
package jode.type; package jode.type;
import jode.AssertError; import jode.AssertError;
import java.util.Stack;
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
/** /**
* This class represents the NullType. The null type is special as it * This class represents the NullType. The null type is special as it
@ -53,7 +57,9 @@ public class NullType extends ReferenceType {
public Type getGeneralizedType(Type type) { public Type getGeneralizedType(Type type) {
if (type.typecode == TC_RANGE) if (type.typecode == TC_RANGE)
type = ((RangeType) type).getTop(); type = ((RangeType) type).getTop();
return type; if (type instanceof ReferenceType)
return type;
return tError;
} }
/** /**
@ -65,13 +71,19 @@ public class NullType extends ReferenceType {
public Type getSpecializedType(Type type) { public Type getSpecializedType(Type type) {
if (type.typecode == TC_RANGE) if (type.typecode == TC_RANGE)
type = ((RangeType) type).getBottom(); type = ((RangeType) type).getBottom();
return type; if (type != tNull)
return tError;
return tNull;
} }
public String toString() { public String toString() {
return "tNull"; return "tNull";
} }
public Type findCommonClassTypes(Stack otherTypes) {
throw new UnsupportedOperationException();
}
/** /**
* Intersect this type with another type and return the new type. * Intersect this type with another type and return the new type.
* @param type the other type. * @param type the other type.

@ -57,9 +57,9 @@ public class RangeType extends Type {
* depends on what ReferenceType class it implements: * depends on what ReferenceType class it implements:
* *
* <dl> * <dl>
* <dt>ClassInterfacesType</dt> * <dt>MultiClassType</dt>
* <dd>All types in this range must be widening castable to all interfaces * <dd>All types in this range must be widening castable to all classes
* and to the class in the bottomType</dd> * in the bottomType</dd>
* <dt>ArrayType</dt> * <dt>ArrayType</dt>
* <dd>All types in this range must be of the bottomType, or the * <dd>All types in this range must be of the bottomType, or the
* NullType.</dd> * NullType.</dd>
@ -214,7 +214,8 @@ public class RangeType extends Type {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) {
GlobalOptions.err.println("intersecting "+ this +" and "+ type + GlobalOptions.err.println("intersecting "+ this +" and "+ type +
" to " + result); " to <" + bottom + "," + top +
"> to " + result);
} }
return result; return result;
} }

@ -21,10 +21,14 @@ package jode.type;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import java.io.IOException; 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 * This is an abstrace super class of all reference types. Reference
* types are ClassInterfacesType, ArrayType and NullType. <p> * types are NullType, MultiClassType, and ClassType with its sub types
* ClassInfoType, SystemClassType, and ArrayType. <p>
* *
* To do intersection on range types, the reference types need three * To do intersection on range types, the reference types need three
* more operations: specialization, generalization and * more operations: specialization, generalization and
@ -56,6 +60,7 @@ public abstract class ReferenceType extends Type {
* @param type the other type. * @param type the other type.
* @return the specialized type. */ * @return the specialized type. */
public abstract Type getSpecializedType(Type type); public abstract Type getSpecializedType(Type type);
/** /**
* Returns the generalized type set of this and type. The result * Returns the generalized type set of this and type. The result
* should be a type set, so that every type, is extended/implemented * 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 * @return the generalized type
*/ */
public abstract Type getGeneralizedType(Type 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 * 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 * contains all types, that extend all types in bottom and are extended

@ -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;
}
}

@ -20,6 +20,7 @@
package jode.type; package jode.type;
import jode.AssertError; import jode.AssertError;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.bytecode.ClassPath;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.util.UnifyHash; import jode.util.UnifyHash;
@ -54,6 +55,8 @@ public class Type {
public static final int TC_UNKNOWN = 101; public static final int TC_UNKNOWN = 101;
public static final int TC_RANGE = 103; public static final int TC_RANGE = 103;
public static final int TC_INTEGER = 107; 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 classHash = new UnifyHash();
private static final UnifyHash arrayHash = 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 public static final Type tBoolByte= new IntegerType(IntegerType.IT_B
| IntegerType.IT_Z); | IntegerType.IT_Z);
public final static ClassType[] EMPTY_IFACES = new ClassType[0];
/** /**
* This type represents the singleton set containing * This type represents the singleton set containing
* <code>java.lang.Object</code>. * <code>java.lang.Object</code>.
*/ */
public static final ClassInterfacesType tObject = public static final SystemClassType tObject =
tClass("java.lang.Object"); tSystemClass("java.lang.Object",
null, EMPTY_IFACES, false, false);
/** /**
* This type represents the singleton set containing the special * This type represents the singleton set containing the special
* null type (the type of null). * null type (the type of null).
@ -147,21 +153,58 @@ public class Type {
* class types, array types, interface types and the null type. * class types, array types, interface types and the null type.
*/ */
public static final Type tUObject = tRange(tObject, tNull); public static final Type tUObject = tRange(tObject, tNull);
/**
* This type represents the singleton set containing
* <code>java.lang.Comparable</code>.
*/
public static final SystemClassType tSerializable =
tSystemClass("java.io.Serializable",
null, EMPTY_IFACES, false, true);
/**
* This type represents the singleton set containing
* <code>java.lang.Comparable</code>.
*/
public static final SystemClassType tCloneable =
tSystemClass("java.lang.Cloneable",
null, EMPTY_IFACES, false, true);
/**
* This type represents the singleton set containing
* <code>java.lang.Comparable</code>.
*/
public static final SystemClassType tComparable =
tSystemClass("java.lang.Comparable",
null, EMPTY_IFACES, false, true);
/** /**
* This type represents the singleton set containing * This type represents the singleton set containing
* <code>java.lang.String</code>. * <code>java.lang.String</code>.
*/ */
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 * This type represents the singleton set containing
* <code>java.lang.StringBuffer</code>. * <code>java.lang.StringBuffer</code>.
*/ */
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 * This type represents the singleton set containing
* <code>java.lang.Class</code>. * <code>java.lang.Class</code>.
*/ */
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 * 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). * @param type the type signature (or method signature).
* @return a singleton set containing the given type. * @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) if (type == null || type.length() == 0)
return tError; return tError;
switch(type.charAt(0)) { switch(type.charAt(0)) {
@ -192,14 +235,14 @@ public class Type {
case 'V': case 'V':
return tVoid; return tVoid;
case '[': case '[':
return tArray(tType(type.substring(1))); return tArray(tType(cp, type.substring(1)));
case 'L': case 'L':
int index = type.indexOf(';'); int index = type.indexOf(';');
if (index != type.length()-1) if (index != type.length()-1)
return tError; return tError;
return tClass(type.substring(1, index)); return tClass(cp, type.substring(1, index));
case '(': case '(':
return tMethod(type); return tMethod(cp, type);
} }
throw new AssertError("Unknown type signature: "+type); throw new AssertError("Unknown type signature: "+type);
} }
@ -210,10 +253,24 @@ public class Type {
* @param clazzname the full qualified name of the class. * @param clazzname the full qualified name of the class.
* The packages may be separated by `.' or `/'. * The packages may be separated by `.' or `/'.
* @return a singleton set containing the given type. * @return a singleton set containing the given type.
* @deprecated Use tClass(ClassInfo)
*/ */
public static final ClassInterfacesType tClass(String clazzname) { public static final ClassType tClass(ClassPath classPath,
return tClass(ClassInfo.forName(clazzname.replace('/','.'))); 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. * @param clazzinfo the jode.bytecode.ClassInfo.
* @return a singleton set containing the given type. * @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(); int hash = clazzinfo.hashCode();
Iterator iter = classHash.iterateHashCode(hash); Iterator iter = classHash.iterateHashCode(hash);
while (iter.hasNext()) { while (iter.hasNext()) {
ClassInterfacesType type = (ClassInterfacesType) iter.next(); ClassInfoType type = (ClassInfoType) iter.next();
if (type.getClassInfo() == clazzinfo) if (type.getClassInfo() == clazzinfo)
return type; return type;
} }
ClassInterfacesType type = new ClassInterfacesType(clazzinfo); ClassInfoType type = new ClassInfoType(clazzinfo);
classHash.put(hash, type); classHash.put(hash, type);
return type; return type;
} }
@ -262,15 +319,16 @@ public class Type {
* @param signature the method decriptor. * @param signature the method decriptor.
* @return a method type (a singleton set). * @return a method type (a singleton set).
*/ */
public static MethodType tMethod(String signature) { public static MethodType tMethod(ClassPath cp, String signature) {
int hash = signature.hashCode(); int hash = signature.hashCode() + cp.hashCode();
Iterator iter = methodHash.iterateHashCode(hash); Iterator iter = methodHash.iterateHashCode(hash);
while (iter.hasNext()) { while (iter.hasNext()) {
MethodType methodType = (MethodType) iter.next(); MethodType methodType = (MethodType) iter.next();
if (methodType.getTypeSignature().equals(signature)) if (methodType.getTypeSignature().equals(signature)
&& methodType.getClassPath().equals(cp))
return methodType; return methodType;
} }
MethodType methodType = new MethodType(signature); MethodType methodType = new MethodType(cp, signature);
methodHash.put(hash, methodType); methodHash.put(hash, methodType);
return 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 * Intersect this set of types with another type set and return the
* intersection. * intersection.

Loading…
Cancel
Save