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. 50
      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. 42
      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. 181
      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. 118
      jode/jode/obfuscator/MethodIdentifier.java
  32. 1647
      jode/jode/obfuscator/modules/ConstantAnalyzer.java
  33. 835
      jode/jode/obfuscator/modules/LocalOptimizer.java
  34. 2
      jode/jode/obfuscator/modules/Makefile.am
  35. 766
      jode/jode/obfuscator/modules/RemovePopAnalyzer.java
  36. 168
      jode/jode/obfuscator/modules/SimpleAnalyzer.java
  37. 156
      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. 11
      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.
* @return true, if the argument parsed without problems.
* @exception IllegalArgumentException
* if a problem occured while parsing the argument.
*/
public static boolean setDebugging(String debuggingString) {
if (debuggingString.length() == 0 || debuggingString.equals("help")) {
usageDebugging();
return false;
throw new IllegalArgumentException();
}
StringTokenizer st = new StringTokenizer(debuggingString, ",");
@ -98,8 +99,8 @@ public class GlobalOptions {
continue next_token;
}
}
err.println("Illegal debugging flag: "+token);
return false;
throw new IllegalArgumentException("Illegal debugging flag: "
+token);
}
return true;
}

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

@ -42,19 +42,26 @@ public class Decompiler {
private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
private int importClassLimit = ImportHandler.DEFAULT_CLASS_LIMIT;
private int tabWidth = 8;
private int indentSize = 4;
private int outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
private int lineWidth = 79;
/**
* We need a different pathSeparatorChar, since ':' (used for most
* UNIX System) is used a protocol separator in URLs.
*
* We currently allow both pathSeparatorChar and
* altPathSeparatorChar and decide if it is a protocol separator
* by context.
* by context.
*/
public static final char altPathSeparatorChar
= ClassPath.altPathSeparatorChar;
/**
* Create a new decompiler.
* Create a new decompiler. Normally you need only one, but you
* can have more around, with different options and different
* class paths.
*/
public Decompiler() {
}
@ -108,14 +115,28 @@ public class Decompiler {
*/
public void setOption(String option, String value) {
if (option.equals("style")) {
if (value.equals("gnu"))
Options.outputStyle = Options.GNU_STYLE;
else if (value.equals("sun"))
Options.outputStyle = Options.SUN_STYLE;
else
if (value.equals("gnu")) {
outputStyle = 0;
indentSize = 2;
} else if (value.equals("sun")) {
outputStyle = TabbedPrintWriter.BRACE_AT_EOL;
indentSize = 4;
} else
throw new IllegalArgumentException("Invalid style "+value);
return;
}
if (option.equals("tabwidth")) {
tabWidth = Integer.parseInt(value);
return;
}
if (option.equals("indent")) {
indentSize = Integer.parseInt(value);
return;
}
if (option.equals("linewidth")) {
lineWidth = Integer.parseInt(value);
return;
}
if (option.equals("import")) {
int comma = value.indexOf(',');
int packLimit = Integer.parseInt(value.substring(0, comma));
@ -135,6 +156,10 @@ public class Decompiler {
GlobalOptions.verboseLevel = Integer.parseInt(value);
return;
}
if (option.equals("debug")) {
GlobalOptions.setDebugging(value);
return;
}
for (int i=0; i < optionStrings.length; i++) {
if (option.equals(optionStrings[i])) {
if (value.equals("0")
@ -184,8 +209,11 @@ public class Decompiler {
ProgressListener progress)
throws java.io.IOException {
if (classPath == null) {
String cp = System.getProperty("java.class.path")
.replace(File.pathSeparatorChar, altPathSeparatorChar);
String cp = System.getProperty("java.class.path");
String bootcp = System.getProperty("sun.boot.class.path");
if (bootcp != null)
cp = bootcp + altPathSeparatorChar + cp;
cp = cp.replace(File.pathSeparatorChar, altPathSeparatorChar);
classPath = new ClassPath(cp);
}
@ -196,7 +224,9 @@ public class Decompiler {
ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit);
TabbedPrintWriter tabbedWriter =
new TabbedPrintWriter(writer, imports, false);
new TabbedPrintWriter(writer, imports, false,
outputStyle, indentSize,
tabWidth, lineWidth);
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
clazzAna.dumpJavaFile(tabbedWriter, progress);
writer.flush();

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

@ -22,7 +22,8 @@ import jode.GlobalOptions;
import jode.bytecode.ClassInfo;
import jode.type.Type;
import jode.type.ArrayType;
import jode.type.ClassInterfacesType;
import jode.type.ClassInfoType;
import jode.type.ClassType;
import jode.type.NullType;
///#def COLLECTIONS java.util
@ -274,7 +275,13 @@ public class ImportHandler {
clazz = outer;
}
String name = clazz.getName();
useClass(clazz.getName());
}
/* Marks the clazz as used, so that it will be imported if used often
* enough.
*/
public void useClass(String name) {
Integer i = (Integer) imports.get(name);
if (i == null) {
@ -308,8 +315,10 @@ public class ImportHandler {
public final void useType(Type type) {
if (type instanceof ArrayType)
useType(((ArrayType) type).getElementType());
else if (type instanceof ClassInterfacesType)
useClass(((ClassInterfacesType) type).getClassInfo());
else if (type instanceof ClassInfoType)
useClass(((ClassInfoType) type).getClassInfo());
else if (type instanceof ClassType)
useClass(((ClassType) type).getClassName());
}
/**
@ -327,7 +336,24 @@ public class ImportHandler {
* @return a legal string representation of clazz.
*/
public String getClassString(ClassInfo clazz) {
String name = clazz.getName();
return getClassString(clazz.getName());
}
/**
* Check if clazz is imported and maybe remove package delimiter from
* full qualified class name.
* <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)
/* We are not yet clean, return the full name */
return name;
@ -358,8 +384,10 @@ public class ImportHandler {
public String getTypeString(Type type) {
if (type instanceof ArrayType)
return getTypeString(((ArrayType) type).getElementType()) + "[]";
else if (type instanceof ClassInterfacesType)
return getClassString(((ClassInterfacesType) type).getClassInfo());
else if (type instanceof ClassInfoType)
return getClassString(((ClassInfoType) type).getClassInfo());
else if (type instanceof ClassType)
return getClassString(((ClassType) type).getClassName());
else if (type instanceof NullType)
return "Object";
else

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

@ -25,6 +25,7 @@ import jode.GlobalOptions;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.zip.ZipOutputStream;
@ -47,7 +48,6 @@ public class Main extends Options {
new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'),
new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'),
new LongOpt("import", LongOpt.REQUIRED_ARGUMENT, null, 'i'),
new LongOpt("style", LongOpt.REQUIRED_ARGUMENT, null, 's'),
new LongOpt("lvt", LongOpt.OPTIONAL_ARGUMENT, null,
OPTION_START+0),
new LongOpt("inner", LongOpt.OPTIONAL_ARGUMENT, null,
@ -86,40 +86,6 @@ public class Main extends Options {
"The directories should be separated by ','.");
err.println(" -d, --dest <dir> "+
"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) {
@ -197,19 +163,6 @@ public class Main extends Options {
errorInParams |= !GlobalOptions.setDebugging(arg);
break;
}
case 's': {
String arg = g.getOptarg();
if ("sun".startsWith(arg))
outputStyle = SUN_STYLE;
else if ("gnu".startsWith(arg))
outputStyle = GNU_STYLE;
else {
GlobalOptions.err.println
("jode.decompiler.Main: Unknown style `"+arg+"'.");
errorInParams = true;
}
break;
}
case 'i': {
String arg = g.getOptarg();
int comma = arg.indexOf(',');
@ -311,6 +264,12 @@ public class Main extends Options {
writer.close();
/* Now is a good time to clean up */
System.gc();
} catch (FileNotFoundException ex) {
GlobalOptions.err.println
("Can't read "+ex.getMessage()+".");
GlobalOptions.err.println
("Check the class path ("+classPathStr+
") and check that you use the java class name.");
} catch (IOException ex) {
GlobalOptions.err.println
("Can't write source of "+params[i]+".");

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

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

@ -22,11 +22,6 @@ import jode.bytecode.ClassInfo;
import java.io.IOException;
public class Options {
public static final int TAB_SIZE_MASK = 0x0f;
public static final int BRACE_AT_EOL = 0x10;
public static final int SUN_STYLE = 0x14;
public static final int GNU_STYLE = 0x02;
public static final int OPTION_LVT = 0x0001;
public static final int OPTION_INNER = 0x0002;
public static final int OPTION_ANON = 0x0004;
@ -42,8 +37,6 @@ public class Options {
OPTION_LVT | OPTION_INNER | OPTION_ANON | OPTION_PRETTY |
OPTION_DECRYPT | OPTION_VERIFY | OPTION_CONTRAFO;
public static int outputStyle = SUN_STYLE;
public final static boolean doAnonymous() {
return (options & OPTION_ANON) != 0;
}

@ -30,30 +30,30 @@ import java.util.Vector;
import java.util.Enumeration;
/**
* A list of local variables that a method scoped class inherits
* from its declaring method.
* <p>A list of local variables that a method scoped class inherits
* from its declaring method.</p>
*
* A method scoped class is a class that is declared in a method and
* it can access other (final) local variables declared earlier. To
* realize this the java compiler adds hidden parameters to the
* <p>A method scoped class is a class that is declared in a method
* and it can access other (final) local variables declared earlier.
* To realize this the java compiler adds hidden parameters to the
* constructor of the method scoped class, where it passes the values
* of the local varaiables. If a method scoped class has more than
* one constructor, each gets this hidden parameters. These hidden
* parameters are the outerValues, because they are used to transport
* a value of a local variable from an outer method.
* a value of a local variable from an outer method.</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
* guess: It first assumes that everything is an outer value parameter
* added by the compiler and if this leads to contradiction shrinks
* the count of these parameters. A contradiction can occur, because
* the constructor is called two times with different values.
* the constructor is called two times with different values.</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
* 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
* values shrink. Sometimes there are real listener queues: if
* another method scoped class creates instances of the first in its
@ -63,28 +63,26 @@ import java.util.Enumeration;
* the second class's constructor is really an outer value, we have to
* add a listener. If later a constructor invokation for the second
* class is found, where a parameter does not have the right outer
* value, the listener will also shrink the outer values list of the
* first class.
* value, the listener will also shrink the outer values list of the
* first class.</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.
* This hidden parameter is not considered as outer value though.
* Note that you can even explicitly invoke the constructor with a
* different outer class instance, by using the
* <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
* last parameter the outer instance of its super class. This should
* really be the first parameter just after the outerValues, as it
* is under javac. We mark such classes as jikesAnonymousInner. This
* is done in the initialize() pass.
* is done in the initialize() pass.</p>
*
* @see #shrinkOuterValues
* @see #addOuterValueListener
* @since 1.0.93
*/
* @since 1.0.93 */
public class OuterValues
{
private ClassAnalyzer clazzAnalyzer;
@ -331,7 +329,6 @@ public class OuterValues
public void setCount(int newHeadCount) {
if (newHeadCount >= headCount)
return;
headCount = newHeadCount;
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0) {
@ -339,6 +336,7 @@ public class OuterValues
new Throwable().printStackTrace(GlobalOptions.err);
}
headCount = newHeadCount;
if (newHeadCount < headMinCount) {
GlobalOptions.err.println
("WARNING: something got wrong with scoped class "
@ -374,5 +372,3 @@ public class OuterValues
return sb.append("]").toString();
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -25,6 +25,7 @@ import jode.bytecode.Reference;
import jode.obfuscator.modules.WildCard;
import jode.obfuscator.modules.MultiIdentifierMatcher;
import jode.obfuscator.modules.SimpleAnalyzer;
import jode.obfuscator.modules.IdentityRenamer;
import java.io.*;
import java.util.zip.ZipOutputStream;
@ -427,27 +428,7 @@ public class ClassBundle implements OptionHandler {
if (postTrafos == null)
postTrafos = new CodeTransformer[0];
if (renamer == null)
renamer = new Renamer() {
public Iterator generateNames(Identifier ident) {
final String base = ident.getName();
return new Iterator() {
int last = 0;
public boolean hasNext() {
return true;
}
public Object next() {
return (last++ == 0 ? base : base + last);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
renamer = new IdentityRenamer();
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory();

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

@ -25,6 +25,8 @@ import jode.AssertError;
import jode.GlobalOptions;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
///#enddef
@ -81,18 +83,14 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
String name;
String type;
Vector usingInstrs = new Vector();
Vector conflictingLocals = new Vector();
String id; // for debugging purposes only.
int size;
int newSlot = -1;
LocalInfo() {
}
LocalInfo(InstrInfo instr) {
usingInstrs.addElement(instr);
}
void conflictsWith(LocalInfo l) {
if (shadow != null) {
getReal().conflictsWith(l);
@ -114,82 +112,114 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
if (this == l)
return;
shadow = l;
if (shadow.name == null) {
shadow.name = name;
shadow.type = type;
}
Enumeration enum = usingInstrs.elements();
while (enum.hasMoreElements()) {
InstrInfo instr = (InstrInfo) enum.nextElement();
instr.local = l;
l.usingInstrs.addElement(instr);
if (l.name == null) {
l.name = name;
l.type = type;
}
if (id.compareTo(l.id) < 0)
l.id = id;
}
public int getFirstAddr() {
int minAddr = Integer.MAX_VALUE;
Enumeration enum = usingInstrs.elements();
while (enum.hasMoreElements()) {
InstrInfo info = (InstrInfo) enum.nextElement();
if (info.instr.getAddr() < minAddr)
minAddr = info.instr.getAddr();
}
return minAddr;
void generateID(int blockNr, int instrNr) {
char[] space = new char[5];
space[0] = (char) ('0' + blockNr / 10);
space[1] = (char) ('0' + blockNr % 10);
space[2] = (char) ('a' + instrNr / (26*26));
space[3] = (char) ('a' + (instrNr / 26) % 26);
space[4] = (char) ('a' + instrNr % 26);
id = new String(space);
}
public String toString() {
return id;
}
}
private static class TodoQueue {
public final InstrInfo LAST = new InstrInfo();
InstrInfo first = LAST;
BlockInfo first = null;
BlockInfo last = null;
public void add(InstrInfo info) {
if (info.nextTodo == null) {
public void add(BlockInfo info) {
if (info.nextTodo == null && info != last) {
/* only enqueue if not already on queue */
info.nextTodo = first;
first = info;
if (first == null)
last = info;
}
}
public boolean isEmpty() {
return first == LAST;
return first == null;
}
public InstrInfo remove() {
if (first == LAST)
throw new NoSuchElementException();
InstrInfo result = first;
public BlockInfo remove() {
BlockInfo result = first;
first = result.nextTodo;
result.nextTodo = null;
if (first == null)
last = null;
return result;
}
}
BasicBlocks bb;
TodoQueue changedInfos;
InstrInfo firstInfo;
LocalInfo[] paramLocals;
BlockInfo[] blockInfos;
int maxlocals;
/**
* This class contains information for each instruction.
*/
static class InstrInfo {
class BlockInfo {
/**
* The next changed InstrInfo, or null, if this instr info did
* The next changed BlockInfo, or null, if this instr info did
* not changed.
*/
InstrInfo nextTodo;
BlockInfo nextTodo;
/**
* The LocalInfo that this instruction manipulates, or null
* if this is not an ret, iinc, load or store instruction.
* The local infos for each Instruction. The index is the
* instruction number.
*/
LocalInfo local;
LocalInfo[] instrLocals;
/**
* The LocalInfo, whose values are read from a previous block.
* Index is the slot number.
*/
LocalInfo[] ins;
/**
* The LocalInfo, written in this block.
* Index is the slot number.
*/
LocalInfo[] gens;
/**
* The predecessors for this block.
*/
Collection preds = new ArrayList();
/**
* The tryBlocks, for which this block is the catcher.
*/
Collection tryBlocks = new ArrayList();
/**
* For each slot, this contains the InstrInfo of one of the
* next Instruction, that may read from that slot, without
* prior writing. */
* prior writing.
*/
InstrInfo[] nextReads;
/**
* This only has a value for ret instructions. In that case
* this bitset contains all locals, that may be used between
* jsr and ret.
* This only has a value for blocks countaining a ret. In
* that case this bitset contains all locals, that may be used
* between jsr and ret.
*/
BitSet usedBySub;
/**
@ -208,27 +238,171 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
* If this instruction is a ret, this contains the single
* allowed jsr target to which this ret belongs.
*/
InstrInfo jsrTargetInfo;
/**
* The Instruction of this info
*/
Instruction instr;
BlockInfo jsrTargetInfo;
/**
* The next info in the chain.
* The underlying basic block.
*/
InstrInfo nextInfo;
}
Block block;
public BlockInfo(int blockNr, Block block) {
this.block = block;
ins = new LocalInfo[bb.getMaxLocals()];
gens = new LocalInfo[bb.getMaxLocals()];
Instruction[] instrs = block.getInstructions();
instrLocals = new LocalInfo[instrs.length];
for (int instrNr = 0; instrNr < instrs.length; instrNr++) {
Instruction instr = instrs[instrNr];
if (instr.hasLocal()) {
int slot = instr.getLocalSlot();
LocalInfo local = new LocalInfo();
instrLocals[instrNr] = local;
LocalVariableInfo lvi = instr.getLocalInfo();
local.name = lvi.getName();
local.type = lvi.getType();
local.size = 1;
local.generateID(blockNr, instrNr);
switch (instr.getOpcode()) {
case opc_lload: case opc_dload:
local.size = 2;
/* fall through */
case opc_iload: case opc_fload: case opc_aload:
case opc_iinc:
/* this is a load instruction */
if (gens[slot] == null) {
ins[slot] = local;
gens[slot] = local;
changedInfos.add(this);
} else {
gens[slot].combineInto(local);
}
break;
BytecodeInfo bc;
case opc_ret:
/* this is a ret instruction */
usedBySub = new BitSet();
if (gens[slot] == null) {
ins[slot] = local;
gens[slot] = local;
changedInfos.add(this);
} else {
gens[slot].combineInto(local);
}
break;
TodoQueue changedInfos;
InstrInfo firstInfo;
Hashtable instrInfos;
boolean produceLVT;
int maxlocals;
case opc_lstore: case opc_dstore:
local.size = 2;
/* fall through */
case opc_istore: case opc_fstore: case opc_astore:
gens[slot] = local;
break;
LocalInfo[] paramLocals;
default:
throw new AssertError
("Illegal opcode for SlotInstruction");
}
}
}
}
void promoteIn(int slot, LocalInfo local) {
if (gens[slot] == null) {
changedInfos.add(this);
ins[slot] = local;
} else {
gens[slot].combineInto(local);
}
}
void promoteInToAll(int slot, LocalInfo local) {
if (ins[slot] == null) {
changedInfos.add(this);
ins[slot] = local;
} else
ins[slot].combineInto(local);
if (gens[slot] != null) {
gens[slot].combineInto(local);
for (int i=0; i< instrLocals.length; i++) {
if (instrLocals[i] != null
&& block.getInstructions()[i].getLocalSlot() == slot)
instrLocals[i].combineInto(local);
}
}
}
public void promoteIns() {
for (int i=0; i < ins.length; i++) {
if (ins[i] != null) {
for (Iterator iter = preds.iterator(); iter.hasNext();) {
BlockInfo pred = (BlockInfo) iter.next();
pred.promoteIn(i, ins[i]);
}
for (Iterator iter = tryBlocks.iterator();
iter.hasNext();) {
BlockInfo pred = (BlockInfo) iter.next();
pred.promoteInToAll(i, ins[i]);
}
}
}
// if (prevInstr.getOpcode() == opc_jsr) {
// /* Prev instr is a jsr, promote reads to the
// * corresponding ret.
// */
// InstrInfo jsrInfo =
// (InstrInfo) instrInfos.get(prevInstr.getSingleSucc());
// if (jsrInfo.retInfo != null) {
// /* Now promote reads that are modified by the
// * subroutine to the ret, and those that are not
// * to the jsr instruction.
// */
// promoteReads(info, jsrInfo.retInfo.instr,
// jsrInfo.retInfo.usedBySub, false);
// promoteReads(info, prevInstr,
// jsrInfo.retInfo.usedBySub, true);
// }
// }
}
public void generateConflicts() {
LocalInfo[] active = (LocalInfo[]) ins.clone();
Instruction[] instrs = block.getInstructions();
for (int instrNr = 0; instrNr < instrs.length; instrNr++) {
Instruction instr = instrs[instrNr];
if (instr.isStore()) {
/* This is a store. It conflicts with every local, which
* is active at this point.
*/
for (int i=0; i < maxlocals; i++) {
if (i != info.instr.getLocalSlot()
&& active[i] != null)
instrLocals[instrNr].conflictsWith(active[i]);
if (info.nextInfo.nextReads[i] != null
&& info.nextInfo.nextReads[i].jsrTargetInfo != null) {
Instruction[] jsrs = info.nextInfo.nextReads[i]
.jsrTargetInfo.instr.getPreds();
for (int j=0; j< jsrs.length; j++) {
InstrInfo jsrInfo
= (InstrInfo) instrInfos.get(jsrs[j]);
for (int k=0; k < maxlocals; k++) {
if (!info.nextInfo.nextReads[i].usedBySub
.get(k)
&& jsrInfo.nextReads[k] != null)
info.local.conflictsWith
(jsrInfo.nextReads[k].local);
}
}
}
}
}
}
}
}
public LocalOptimizer() {
}
@ -254,108 +428,41 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
return result;
}
void promoteReads(InstrInfo info, Instruction preInstr,
BitSet mergeSet, boolean inverted) {
InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr);
int omitLocal = -1;
if (preInstr.getOpcode() >= opc_istore
&& preInstr.getOpcode() <= opc_astore) {
/* This is a store */
omitLocal = preInstr.getLocalSlot();
if (info.nextReads[omitLocal] != null)
preInfo.local.combineInto(info.nextReads[omitLocal].local);
}
for (int i=0; i < maxlocals; i++) {
if (info.nextReads[i] != null && i != omitLocal
&& (mergeSet == null || mergeSet.get(i) != inverted)) {
if (preInfo.nextReads[i] == null) {
preInfo.nextReads[i] = info.nextReads[i];
changedInfos.add(preInfo);
} else {
preInfo.nextReads[i].local
.combineInto(info.nextReads[i].local);
}
}
}
}
void promoteReads(InstrInfo info, Instruction preInstr) {
promoteReads(info, preInstr, null, false);
}
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt,
int slot, int addr) {
LocalVariableInfo match = null;
for (int i=0; i < lvt.length; i++) {
if (lvt[i].slot == slot
&& lvt[i].start.getAddr() <= addr
&& lvt[i].end.getAddr() >= addr) {
if (match != null
&& (!match.name.equals(lvt[i].name)
|| !match.type.equals(lvt[i].type))) {
/* Multiple matches..., give no info */
return null;
}
match = lvt[i];
}
}
return match;
}
public LocalVariableInfo findLVTEntry(LocalVariableInfo[] lvt,
Instruction instr) {
int addr;
if (instr.getOpcode() >= opc_istore
&& instr.getOpcode() <= opc_astore)
addr = instr.getNextAddr();
else
addr = instr.getAddr();
return findLVTEntry(lvt, instr.getLocalSlot(), addr);
}
public void calcLocalInfo() {
maxlocals = bc.getMaxLocals();
Handler[] handlers = bc.getExceptionHandlers();
LocalVariableInfo[] lvt = bc.getLocalVariableTable();
if (lvt != null)
produceLVT = true;
maxlocals = bb.getMaxLocals();
Block[] blocks = bb.getBlocks();
/* Initialize paramLocals */
{
String methodType = bc.getMethodInfo().getType();
int paramCount = (bc.getMethodInfo().isStatic() ? 0 : 1)
String methodType = bb.getMethodInfo().getType();
int paramCount = (bb.getMethodInfo().isStatic() ? 0 : 1)
+ TypeSignature.getArgumentSize(methodType);
paramLocals = new LocalInfo[paramCount];
int slot = 0;
if (!bc.getMethodInfo().isStatic()) {
if (!bb.getMethodInfo().isStatic()) {
LocalInfo local = new LocalInfo();
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, 0, 0);
if (lvi != null) {
local.name = lvi.name;
local.type = lvi.type;
}
}
LocalVariableInfo lvi = bb.getParamInfo(slot);
local.type = "L" + (bb.getMethodInfo().getClazzInfo()
.getName().replace('.', '/'))+";";
if (local.type.equals(lvi.getType()))
local.name = lvi.getName();
local.size = 1;
local.id = " this";
paramLocals[slot++] = local;
}
int pos = 1;
while (pos < methodType.length()
&& methodType.charAt(pos) != ')') {
LocalInfo local = new LocalInfo();
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, slot, 0);
if (lvi != null) {
local.name = lvi.name;
}
}
int start = pos;
pos = TypeSignature.skipType(methodType, pos);
LocalInfo local = new LocalInfo();
LocalVariableInfo lvi = bb.getParamInfo(slot);
local.type = methodType.substring(start, pos);
if (local.type.equals(lvi.getType()))
local.name = lvi.getName();
local.size = TypeSignature.getTypeSize(local.type);
local.id = " parm";
paramLocals[slot] = local;
slot += local.size;
}
@ -364,97 +471,28 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
/* Initialize the InstrInfos and LocalInfos
*/
changedInfos = new TodoQueue();
instrInfos = new Hashtable();
{
InstrInfo info = firstInfo = new InstrInfo();
Iterator i = bc.getInstructions().iterator();
while (true) {
Instruction instr = (Instruction) i.next();
instrInfos.put(instr, info);
info.instr = instr;
info.nextReads = new InstrInfo[maxlocals];
if (instr.hasLocalSlot()) {
info.local = new LocalInfo(info);
if (lvt != null) {
LocalVariableInfo lvi = findLVTEntry(lvt, instr);
if (lvi != null) {
info.local.name = lvi.name;
info.local.type = lvi.type;
}
}
info.local.size = 1;
switch (instr.getOpcode()) {
case opc_lload: case opc_dload:
info.local.size = 2;
/* fall through */
case opc_iload: case opc_fload: case opc_aload:
case opc_iinc:
/* this is a load instruction */
info.nextReads[instr.getLocalSlot()] = info;
changedInfos.add(info);
break;
blockInfos = new BlockInfo[blocks.length];
for (int i=0; i< blocks.length; i++)
blockInfos[i] = new BlockInfo(i, blocks[i]);
case opc_ret:
/* this is a ret instruction */
info.usedBySub = new BitSet();
info.nextReads[instr.getLocalSlot()] = info;
changedInfos.add(info);
break;
case opc_lstore: case opc_dstore:
info.local.size = 2;
//case opc_istore: case opc_fstore: case opc_astore:
}
}
if (!i.hasNext())
break;
info = info.nextInfo = new InstrInfo();
for (int i=0; i< blocks.length; i++) {
int[] succs = blocks[i].getSuccs();
for (int j=0; j< succs.length; j++) {
if (succs[j] >= 0)
blockInfos[succs[j]].preds.add(blockInfos[i]);
}
BasicBlocks.Handler[] handlers = blocks[i].getCatcher();
for (int j=0; j< handlers.length; j++) {
blockInfos[handlers[j].getCatcher()]
.tryBlocks.add(blockInfos[i]);
}
}
/* find out which locals are the same.
*/
while (!changedInfos.isEmpty()) {
InstrInfo info = changedInfos.remove();
Instruction instr = info.instr;
/* Mark the local as used in all ret instructions */
if (instr.hasLocalSlot()) {
int slot = instr.getLocalSlot();
for (int i=0; i< maxlocals; i++) {
InstrInfo retInfo = info.nextReads[i];
if (retInfo != null
&& retInfo.instr.getOpcode() == opc_ret
&& !retInfo.usedBySub.get(slot)) {
retInfo.usedBySub.set(slot);
if (retInfo.jsrTargetInfo != null)
changedInfos.add(retInfo.jsrTargetInfo);
}
}
}
Instruction prevInstr = instr.getPrevByAddr();
if (prevInstr != null) {
if (!prevInstr.doesAlwaysJump())
promoteReads(info, prevInstr);
else if (prevInstr.getOpcode() == opc_jsr) {
/* Prev instr is a jsr, promote reads to the
* corresponding ret.
*/
InstrInfo jsrInfo =
(InstrInfo) instrInfos.get(prevInstr.getSingleSucc());
if (jsrInfo.retInfo != null) {
/* Now promote reads that are modified by the
* subroutine to the ret, and those that are not
* to the jsr instruction.
*/
promoteReads(info, jsrInfo.retInfo.instr,
jsrInfo.retInfo.usedBySub, false);
promoteReads(info, prevInstr,
jsrInfo.retInfo.usedBySub, true);
}
}
}
BlockInfo info = changedInfos.remove();
info.promoteIns();
if (instr.getPreds() != null) {
for (int i = 0; i < instr.getPreds().length; i++) {
@ -500,48 +538,45 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
promoteReads(info, instr.getPreds()[i]);
}
}
for (int i=0; i < handlers.length; i++) {
if (handlers[i].catcher == instr) {
for (Instruction preInstr = handlers[i].start;
preInstr != handlers[i].end.getNextByAddr();
preInstr = preInstr.getNextByAddr()) {
promoteReads(info, preInstr);
}
}
}
}
changedInfos = null;
/* Now merge with the parameters
* The params should be the locals in firstInfo.nextReads
*/
for (int i=0; i< paramLocals.length; i++) {
if (firstInfo.nextReads[i] != null) {
firstInfo.nextReads[i].local.combineInto(paramLocals[i]);
paramLocals[i] = paramLocals[i].getReal();
int startBlock = bb.getStartBlock();
if (startBlock >= 0) {
LocalInfo[] ins = blockInfos[startBlock].ins;
for (int i=0; i< paramLocals.length; i++) {
if (ins[i] != null)
paramLocals[i].combineInto(ins[i]);
}
}
}
public void stripLocals() {
ListIterator iter = bc.getInstructions().listIterator();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
Instruction instr = (Instruction) iter.next();
if (info.local != null && info.local.usingInstrs.size() == 1) {
/* If this is a store, whose value is never read; it can
* be removed, i.e replaced by a pop. */
switch (instr.getOpcode()) {
case opc_istore:
case opc_fstore:
case opc_astore:
iter.set(new Instruction(opc_pop));
break;
case opc_lstore:
case opc_dstore:
iter.set(new Instruction(opc_pop2));
break;
default:
Block[] blocks = bb.getBlocks();
for (int i = 0; i < blocks.length; i++) {
Instruction[] instrs = blocks[i].getInstructions();
for (int j = 0; j < instrs.length; j++) {
Instruction instr = instrs[j];
if (info.local != null
&& info.local.usingBlocks.size() == 1) {
/* If this is a store, whose value is never read; it can
* be removed, i.e replaced by a pop.
*/
switch (instr.getOpcode()) {
case opc_istore:
case opc_fstore:
case opc_astore:
instrs[j] = Instruction.forOpcode(opc_pop);
break;
case opc_lstore:
case opc_dstore:
instrs[j] = Instruction.forOpcode(opc_pop2);
break;
default:
}
}
}
}
@ -611,38 +646,8 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
/* Now calculate the conflict settings.
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.instr.getOpcode() >= BytecodeInfo.opc_istore
&& info.instr.getOpcode() <= BytecodeInfo.opc_astore) {
/* This is a store. It conflicts with every local, whose
* value will be read without write.
*
* If this is inside a ret, it also conflicts with
* locals, that are not used inside, and where any jsr
* would conflict with.
*/
for (int i=0; i < maxlocals; i++) {
if (i != info.instr.getLocalSlot()
&& info.nextReads[i] != null)
info.local.conflictsWith(info.nextReads[i].local);
if (info.nextInfo.nextReads[i] != null
&& info.nextInfo.nextReads[i].jsrTargetInfo != null) {
Instruction[] jsrs = info.nextInfo.nextReads[i]
.jsrTargetInfo.instr.getPreds();
for (int j=0; j< jsrs.length; j++) {
InstrInfo jsrInfo
= (InstrInfo) instrInfos.get(jsrs[j]);
for (int k=0; k < maxlocals; k++) {
if (!info.nextInfo.nextReads[i].usedBySub
.get(k)
&& jsrInfo.nextReads[k] != null)
info.local.conflictsWith
(jsrInfo.nextReads[k].local);
}
}
}
}
}
for (int blockNr = 0; blockNr < blockInfos.length; blockNr++) {
blockInfos[blockNr].generateConflicts();
}
/* Now put the locals that need a color into a vector.
@ -670,11 +675,6 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
}
}
bc.setMaxLocals(maxlocals);
/* Update LocalVariableTable
*/
if (produceLVT)
buildNewLVT();
}
private InstrInfo CONFLICT = new InstrInfo();
@ -704,222 +704,63 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
return changed;
}
public void buildNewLVT() {
/* First we recalculate the usedBySub, to use the new local numbers.
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo)
if (info.usedBySub != null)
info.usedBySub = new BitSet();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.local != null) {
for (int i=0; i < info.nextReads.length; i++) {
if (info.nextReads[i] != null
&& info.nextReads[i].instr.getOpcode() == opc_ret)
info.nextReads[i].usedBySub.set(info.local.newSlot);
}
}
}
/* Now we begin with the first Instruction and follow program flow.
* We remember which locals are life in lifeLocals.
*/
firstInfo.lifeLocals = new LocalInfo[maxlocals];
for (int i=0; i < paramLocals.length; i++)
firstInfo.lifeLocals[i] = paramLocals[i];
Stack changedInfo = new Stack();
changedInfo.push(firstInfo);
Handler[] handlers = bc.getExceptionHandlers();
while (!changedInfo.isEmpty()) {
InstrInfo info = (InstrInfo) changedInfo.pop();
Instruction instr = info.instr;
LocalInfo[] newLife = info.lifeLocals;
if (instr.hasLocalSlot()) {
int slot = instr.getLocalSlot();
LocalInfo instrLocal = info.local.getReal();
newLife = (LocalInfo[]) newLife.clone();
newLife[slot] = instrLocal;
if (instrLocal.name != null) {
for (int j=0; j< newLife.length; j++) {
if (j != slot
&& newLife[j] != null
&& instrLocal.name.equals(newLife[j].name)) {
/* This local changed the slot. */
newLife[j] = null;
}
}
}
}
if (!instr.doesAlwaysJump()) {
InstrInfo nextInfo = info.nextInfo;
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
if (instr.hasSuccs()) {
Instruction[] succs = instr.getSuccs();
for (int i = 0; i < succs.length; i++) {
InstrInfo nextInfo
= (InstrInfo) instrInfos.get(succs[i]);
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
}
for (int i=0; i < handlers.length; i++) {
if (handlers[i].start.compareTo(instr) <= 0
&& handlers[i].end.compareTo(instr) >= 0) {
InstrInfo nextInfo
= (InstrInfo) instrInfos.get(handlers[i].catcher);
if (promoteLifeLocals(newLife, nextInfo))
changedInfo.push(nextInfo);
}
}
if (info.instr.getOpcode() == opc_jsr) {
/* On a jsr we do a special merge */
Instruction jsrTargetInstr = info.instr.getSingleSucc();
InstrInfo jsrTargetInfo
= (InstrInfo) instrInfos.get(jsrTargetInstr);
InstrInfo retInfo = jsrTargetInfo.retInfo;
if (retInfo != null && retInfo.lifeLocals != null) {
LocalInfo[] retLife = (LocalInfo[]) newLife.clone();
for (int i=0; i< maxlocals; i++) {
if (retInfo.usedBySub.get(i))
retLife[i] = retInfo.lifeLocals[i];
}
if (promoteLifeLocals(retLife, info.nextInfo))
changedInfo.push(info.nextInfo);
}
}
if (info.jsrTargetInfo != null) {
/* On a ret we do a special merge */
Instruction jsrTargetInstr = info.jsrTargetInfo.instr;
for (int j=0; j< jsrTargetInstr.getPreds().length; j++) {
InstrInfo jsrInfo
= (InstrInfo) instrInfos.get(jsrTargetInstr.getPreds()[j]);
if (jsrInfo.lifeLocals == null)
/* life locals are not calculated, yet */
continue;
LocalInfo[] retLife = (LocalInfo[]) newLife.clone();
for (int i=0; i< maxlocals; i++) {
if (!info.usedBySub.get(i))
retLife[i] = jsrInfo.lifeLocals[i];
}
if (promoteLifeLocals(retLife, jsrInfo.nextInfo))
changedInfo.push(jsrInfo.nextInfo);
}
}
}
Vector lvtEntries = new Vector();
LocalVariableInfo[] lvi = new LocalVariableInfo[maxlocals];
LocalInfo[] currentLocal = new LocalInfo[maxlocals];
for (int i=0; i< paramLocals.length; i++) {
if (paramLocals[i] != null) {
currentLocal[i] = paramLocals[i];
if (currentLocal[i].name != null) {
lvi[i] = new LocalVariableInfo();
lvtEntries.addElement(lvi[i]);
lvi[i].name = currentLocal[i].name; /* XXX obfuscation? */
lvi[i].type = Main.getClassBundle()
.getTypeAlias(currentLocal[i].type);
lvi[i].start = (Instruction) bc.getInstructions().get(0);
lvi[i].slot = i;
}
}
}
Instruction lastInstr = null;
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
for (int i=0; i< maxlocals; i++) {
LocalInfo lcl = info.lifeLocals != null ? info.lifeLocals[i]
: null;
if (lcl != currentLocal[i]
&& (lcl == null || currentLocal[i] == null
|| lcl.name == null || lcl.type == null
|| !lcl.name.equals(currentLocal[i].name)
|| !lcl.type.equals(currentLocal[i].type))) {
if (lvi[i] != null) {
lvi[i].end = info.instr.getPrevByAddr();
}
lvi[i] = null;
currentLocal[i] = lcl;
if (currentLocal[i] != null
&& currentLocal[i].name != null
&& currentLocal[i].type != null) {
lvi[i] = new LocalVariableInfo();
lvtEntries.addElement(lvi[i]);
lvi[i].name = currentLocal[i].name;
lvi[i].type = Main.getClassBundle()
.getTypeAlias(currentLocal[i].type);
lvi[i].start = info.instr;
lvi[i].slot = i;
}
}
}
lastInstr = info.instr;
}
for (int i=0; i< maxlocals; i++) {
if (lvi[i] != null)
lvi[i].end = lastInstr;
}
LocalVariableInfo[] lvt = new LocalVariableInfo[lvtEntries.size()];
lvtEntries.copyInto(lvt);
bc.setLocalVariableTable(lvt);
}
public void dumpLocals() {
Vector locals = new Vector();
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
GlobalOptions.err.println(info.instr.getDescription());
GlobalOptions.err.print("nextReads: ");
for (int i=0; i<maxlocals; i++)
if (info.nextReads[i] == null)
GlobalOptions.err.print("-,");
for (int blockNr=0; blockNr < blockInfos.length; blockNr++) {
BlockInfo info = blockInfos[blockNr];
GlobalOptions.err.print("ins: [");
for (int i=0; i<maxlocals; i++) {
if (info.ins[i] == null)
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
GlobalOptions.err.print(info.nextReads[i].instr.getAddr()+",");
GlobalOptions.err.print(info.ins[i].toString()+",");
}
GlobalOptions.err.println("]");
if (info.usedBySub != null)
GlobalOptions.err.print(" usedBySub: "+info.usedBySub);
GlobalOptions.err.println(" usedBySub: "+info.usedBySub);
if (info.retInfo != null)
GlobalOptions.err.print(" ret info: "
GlobalOptions.err.println(" ret info: "
+info.retInfo.instr.getAddr());
if (info.jsrTargetInfo != null)
GlobalOptions.err.print(" jsr info: "
GlobalOptions.err.println(" jsr info: "
+info.jsrTargetInfo.instr.getAddr());
GlobalOptions.err.println();
if (info.local != null && !locals.contains(info.local))
locals.addElement(info.local);
}
Enumeration enum = locals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo) enum.nextElement();
int slot = ((InstrInfo)li.usingInstrs.elementAt(0))
.instr.getLocalSlot();
GlobalOptions.err.print("Slot: "+slot+" conflicts:");
Enumeration enum1 = li.conflictingLocals.elements();
while (enum1.hasMoreElements()) {
LocalInfo cfl = (LocalInfo)enum1.nextElement();
GlobalOptions.err.print(cfl.getFirstAddr()+", ");
}
GlobalOptions.err.println();
GlobalOptions.err.print(li.getFirstAddr());
GlobalOptions.err.print(" instrs: ");
Enumeration enum2 = li.usingInstrs.elements();
while (enum2.hasMoreElements())
GlobalOptions.err.print(((InstrInfo)enum2.nextElement())
.instr.getAddr()+", ");
GlobalOptions.err.println();
}
GlobalOptions.err.println("-----------");
// Enumeration enum = locals.elements();
// while (enum.hasMoreElements()) {
// LocalInfo li = (LocalInfo) enum.nextElement();
// int slot = ((InstrInfo)li.usingInstrs.elementAt(0))
// .instr.getLocalSlot();
// GlobalOptions.err.print("Slot: "+slot+" conflicts:");
// Enumeration enum1 = li.conflictingLocals.elements();
// while (enum1.hasMoreElements()) {
// LocalInfo cfl = (LocalInfo)enum1.nextElement();
// GlobalOptions.err.print(cfl.getAddr()+", ");
// }
// GlobalOptions.err.println();
// GlobalOptions.err.print(li.getAddr());
// GlobalOptions.err.print(" instrs: ");
// Enumeration enum2 = li.usingInstrs.elements();
// while (enum2.hasMoreElements())
// GlobalOptions.err.print(((InstrInfo)enum2.nextElement())
// .instr.getAddr()+", ");
// GlobalOptions.err.println();
// }
// GlobalOptions.err.println("-----------");
}
public void transformCode(BytecodeInfo bytecode) {
this.bc = bytecode;
public void transformCode(BasicBlocks bb) {
this.bb = bb;
calcLocalInfo();
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_LOCALS) != 0) {

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

@ -23,288 +23,568 @@ import jode.obfuscator.*;
import jode.AssertError;
import jode.GlobalOptions;
import java.util.BitSet;
///#def COLLECTIONS java.util
import java.util.ListIterator;
import java.util.LinkedList;
import java.util.Stack;
///#enddef
public class RemovePopAnalyzer implements CodeTransformer, Opcodes {
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];
ListIterator iter = bytecode.getInstructions().listIterator();
next_pop:
while (iter.hasNext()) {
Instruction popInstr = (Instruction) iter.next();
boolean isPop2 = false;
switch (popInstr.getOpcode()) {
case opc_nop: {
iter.remove();
continue;
}
int maxStack = bb.getMaxStack();
int poppedLen = maxStack >> 5;
case opc_pop2:
isPop2 = true;
case opc_pop:
if (popInstr.getPreds() != null)
// Can't handle pop with multiple predecessors
continue next_pop;
Handler[] handlers = bytecode.getExceptionHandlers();
for (int i=0; i < handlers.length; i++)
if (handlers[i].catcher == popInstr)
continue next_pop;
// remove pop, we will insert it again if something
// bad happened.
iter.remove();
// remember position of pop, so we can insert it again.
Instruction popPrevious = (Instruction) iter.previous();
Instruction instr = popPrevious;
int count = 0;
while (true) {
if (instr.getSuccs() != null
|| instr.doesAlwaysJump()) {
instr = null;
break;
}
instr.getStackPopPush(poppush);
for (int i = 0; i < blocks.length; i++) {
BitSet popped = new BitSet();
Instruction[] instrs = blocks[i].getInstructions();
int[] removed = instrs.length >> 5;
if (count < poppush[1]) {
if (count == 0)
break;
// Be conservative with stack depth at end of block
int depth = maxStack;
int opcode = instr.getOpcode();
/* If this is a dup and the instruction popped is the
* duplicated element, remove the dup and the pop
*/
if (count <= 3 && opcode == (opc_dup + count - 1)) {
iter.remove();
if (!isPop2)
continue next_pop;
// We have to consider a pop instead of a
// pop2 now.
popInstr = new Instruction(opc_pop);
isPop2 = false;
instr = (Instruction) iter.previous();
continue;
}
if (isPop2
&& count > 1 && count <= 4
&& opcode == (opc_dup2 + count-2)) {
iter.remove();
continue next_pop;
}
/* Otherwise popping is not possible */
instr = null;
break;
for (int j = instrs.length; j-- > 0; ) {
Instruction instr = instrs[j];
instr.getStackPopPush(poppush);
// Now check if the parameters of this instr are needed.
boolean params_needed = false;
switch (instr.getOpcode()) {
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_checkcast:
/* These instructions have side effects, parameters
* are always needed. Adjust depth.
*/
params_needed = true;
depth += poppush[1];
break;
default:
/* Check if all results are needed, adjust depth */
for (int k = 0; k < poppush[1]; k++) {
if (!popped.get(depth++))
params_needed = true;
}
count += poppush[0] - poppush[1];
instr = (Instruction) iter.previous();
}
if (instr == null) {
// We insert the pop at the previous position
while (iter.next() != popPrevious)
{}
if (!isPop2 && popPrevious.getOpcode() == opc_pop) {
// merge pop with popPrevious
iter.set(new Instruction(opc_pop2));
} else
iter.add(popInstr);
continue;
if (params_needed) {
/* mark params as needed */
for (int k = 0; k < poppush[0]; k++)
popped.clear(--depth);
} else {
removed[j >> 5] |= 1 << (j & 31);
/* mark params as popped */
for (int k = 0; k < poppush[0]; k++)
popped.set(--depth);
}
}
int[] poppedArr = new int[poppedLen];
if (blocks[i] != bb.getStartBlock()) {
/* Only can pop entries before this block if
* this isn't the start block.
*/
for (int k = 0; k < block_pop; k++) {
if (popped.get(depth+k))
poppedArr[k >> 5] |= 1 << (k & 31);
}
int opcode = instr.getOpcode();
switch (opcode) {
case opc_ldc2_w:
case opc_lload: case opc_dload:
if (!isPop2)
throw new AssertError("pop on long");
iter.remove();
}
infos[i] = new BlockInfo(poppedArr, removed);
}
/* Now start sharing poppedEntries as necessary. */
int[] empty = new int[poppedLen];
next_block:
for (int i = 1; i < blocks.length; i++) {
/* Try to find an earlier block with a same predecessor. */
for (int j = 0; j < blocks.length; j++) {
Block[] succs = blocks[j].getSuccs();
if (succs.length < 2)
continue;
case opc_ldc:
case opc_iload: case opc_fload: case opc_aload:
case opc_dup:
case opc_new:
if (isPop2)
iter.set(new Instruction(opc_pop));
else
iter.remove();
int blockNr = -1;
boolean isAPred = false;
for (int k = 0; k < succs.length; k++) {
if (succs[k] == blocks[i])
isAPred = true;
if (succs[k] != null && succs[k].getBlockNr() < i)
blockNr = succs[k].getBlockNr();
if (isAPred && blockNr >= 0) {
int[] common = infos[blockNr].poppedEntries;
int[] my = infos[i].poppedEntries;
for (int k = 0; k < poppedLen; k++)
common[k] &= my[k];
infos[i].poppedEntries = common;
continue next_block;
}
}
}
}
/* Now propagate poppedEntries through blocks */
boolean changed = true;
while (changed) {
changed = false;
next_block:
for (int i = 0; i < blocks.length; i++) {
Block[] succs = blocks[i].getSuccs();
int[] succPops = null;
for (int j = 0; j < succs.length; j++) {
if (succs[j] != null) {
succPops = infos[succs[j].getBlockNr()].poppedEntries;
}
}
if (succPops == null)
continue;
case opc_iaload: case opc_faload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload:
case opc_iadd: case opc_fadd:
case opc_isub: case opc_fsub:
case opc_imul: case opc_fmul:
case opc_idiv: case opc_fdiv:
case opc_irem: case opc_frem:
case opc_iand: case opc_ior : case opc_ixor:
case opc_ishl: case opc_ishr: case opc_iushr:
case opc_fcmpl: case opc_fcmpg:
/* We have to pop one entry more. */
iter.next();
iter.add(popInstr);
iter.previous();
iter.previous();
iter.set(new Instruction(opc_pop));
blocks[i].getStackPopPush(poppush);
int[] myPops = infos[i].poppedPush;
for (int k = poppush[1]; k < maxStack; k++) {
if (succs.length == 0 || succs[0] == null)
continue;
int[] succsPoppedEntries
= infos[succs[0].getBlockNr()].poppedEntries;
for (int j = 0; j < succs.length; j++) {
if (succs[j] == null)
continue next_block;
int[] thisPopped
= infos[succs[j].getBlockNr()].poppedEntries;
case opc_dup_x1:
iter.set(new Instruction(opc_swap));
iter.next();
if (isPop2)
iter.add(new Instruction(opc_pop));
continue;
for (int k = 0; k < poppedLen; k++) {
succsPoppedEntries &=
case opc_dup2:
if (isPop2) {
iter.remove();
continue;
}
break;
case opc_swap:
if (isPop2) {
iter.set(popInstr);
continue;
}
for (int j = instrs.length; j-- > 0; ) {
Instruction instr = instrs[j];
instr.getStackPopPush(poppush);
// Now check if the parameters of this instr are needed.
boolean params_needed = false;
switch (instr.getOpcode()) {
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_checkcast:
/* These instructions have side effects, parameters
* are always needed. Adjust depth.
*/
params_needed = true;
depth += poppush[1];
break;
case opc_lneg: case opc_dneg:
case opc_l2d: case opc_d2l:
case opc_laload: case opc_daload:
if (!isPop2)
throw new AssertError("pop on long");
/* fall through */
case opc_ineg: case opc_fneg:
case opc_i2f: case opc_f2i:
case opc_i2b: case opc_i2c: case opc_i2s:
case opc_newarray: case opc_anewarray:
case opc_arraylength:
case opc_instanceof:
iter.set(popInstr);
continue;
case opc_pop:
case opc_pop2:
break;
case opc_l2i: case opc_l2f:
case opc_d2i: case opc_d2f:
if (isPop2) {
iter.next();
iter.add(new Instruction(opc_pop));
iter.previous();
iter.previous();
default:
/* Check if all results are needed, adjust depth */
for (int k = 0; k < poppush[1]; k++) {
if (!popped.get(depth++))
params_needed = true;
}
iter.set(new Instruction(opc_pop2));
continue;
/* If opcode has no result it has side effects. */
if (poppush[1] == 0)
params_needed = true;
}
if (block_pop < maxStack - depth)
block_pop = maxStack - depth;
case opc_ladd: case opc_dadd:
case opc_lsub: case opc_dsub:
case opc_lmul: case opc_dmul:
case opc_ldiv: case opc_ddiv:
case opc_lrem: case opc_drem:
case opc_land: case opc_lor : case opc_lxor:
if (!isPop2)
throw new AssertError("pop on long");
iter.next();
iter.add(popInstr);
iter.previous();
iter.previous();
iter.set(new Instruction(opc_pop2));
continue;
case opc_lshl: case opc_lshr: case opc_lushr:
if (!isPop2)
throw new AssertError("pop on long");
iter.next();
iter.add(popInstr);
iter.previous();
iter.previous();
iter.set(new Instruction(opc_pop));
continue;
if (params_needed) {
/* mark params as needed */
for (int k = 0; k < poppush[0]; k++)
popped.clear(--depth);
} else {
removed[j >> 5] |= 1 << (j & 31);
/* mark params as popped */
for (int k = 0; k < poppush[0]; k++)
popped.set(--depth);
}
}
case opc_i2l: case opc_i2d:
case opc_f2l: case opc_f2d:
if (!isPop2)
throw new AssertError("pop on long");
iter.set(new Instruction(opc_pop));
if (blocks[j] == null)
;
}
}
}
return infos;
}
public void transformCode(BasicBlocks bb) {
if (bb.getStartBlock() == null)
return;
BlockInfo[] infos = calcBlockInfos(bb);
int poppush[] = new int[2];
boolean poppedEntries[] = new boolean[bb.getMaxStack()];
Block[] blocks = bb.getBlocks();
for (int i = 0; i < blocks.length; i++) {
LinkedList newInstructions = new LinkedList();
Instruction[] oldInstrs = blocks[i].getInstructions();
int instrNr = oldInstrs.length;
int stackDepth = 0;
while (instrNr > 0) {
Instruction instr = oldInstrs[instrNr];
if (instr.getOpcode() == opc_nop)
continue;
case opc_lcmp:
case opc_dcmpl: case opc_dcmpg:
iter.next();
iter.add(new Instruction(opc_pop2));
if (isPop2) {
iter.add(new Instruction(opc_pop));
iter.previous();
}
iter.previous();
iter.previous();
iter.set(new Instruction(opc_pop2));
if (instr.getOpcode() == opc_pop) {
poppedEntries[stackDepth++] = true;
continue;
case opc_getstatic:
case opc_getfield: {
Reference ref = instr.getReference();
int size = TypeSignature.getTypeSize(ref.getType());
if (size == 2 && !isPop2)
throw new AssertError("pop on long");
if (opcode == opc_getfield)
size--;
switch (size) {
case 0:
iter.set(popInstr);
break;
case 1:
if (isPop2) {
iter.set(new Instruction(opc_pop));
break;
}
/* fall through */
case 2:
iter.remove();
}
}
if (instr.getOpcode() == opc_pop2) {
poppedEntries[stackDepth++] = true;
poppedEntries[stackDepth++] = true;
continue;
}
case opc_multianewarray: {
int dims = instr.getDimensions();
if (--dims > 0) {
iter.next();
while (dims-- > 0) {
iter.add(new Instruction(opc_pop));
iter.previous();
}
iter.previous();
}
iter.set(popInstr);
continue;
instr.getStackPopPush(poppush);
// First look if stackDepth was right
if (stackDepth < poppush[1]) {
int diff = poppush[1] - stackDepth;
System.arraycopy(poppedEntries, 0,
poppedEntries, diff, stackDepth);
for (int j=0; j< diff; i++)
poppedEntries[j] = false;
}
// Now check if this instr pushes a popped Entry.
boolean push_a_popped = false;
boolean push_all_popped = true;
for (int j=0; j < poppush[1]; j++) {
if (poppedEntries[j])
push_a_popped = true;
else
push_all_popped = false;
}
if (push_a_popped) {
/* We push an entry, that gets popped later */
int opcode = instr.getOpcode();
switch (opcode) {
case opc_dup:
case opc_dup_x1:
case opc_dup_x2:
case opc_dup2:
case opc_dup2_x1:
case opc_dup2_x2: {
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
if (TypeSignature.getReturnSize
(instr.getReference().getType()) != 1)
int count = (opcode - opc_dup)/3+1;
int depth = (opcode - opc_dup)%3;
stackDepth -= count;
int bottom = stackDepth - count - depth;
int popped1 = 0;
int popped3 = 0;
int newDepth = 0;
// get the popped mask and adjust poppedEntries.
for (int j=0; j< count; j++) {
if (poppedEntries[bottom + j])
popped1 |= 1<<j;
if (poppedEntries[bottom + count + depth + j])
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;
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;
}
if (popped1 == 1) {
// count == 2, popped1 = 1,
depth++;
count--;
popped1 = 0;
popped3 >>= 1;
}
if (count == 2 && popped1 == 0 && popped3 > 0) {
// Do the normal dup2 and pop the right
// element afterwards.
if (popped3 == 3) {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop2));
} else {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop));
if (popped3 == 1)
newInstructions.addFirst
(Instruction.forOpcode(opc_swap));
}
popped3 = 0;
}
if (popped1 == popped3) {
// popped1 == popped3 == 0
// Do a dupcount_xdepth now.
if (depth < 3) {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop - 3 +
depth + 3 * count));
break;
} else {
// I hope that this will almost never happen.
// depth = 3, count = 1;
// Note that instructions are backwards.
newInstructions.addFirst
(Instruction.forOpcode(opc_pop2)); //DABCD
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x2)); //DABCDAB
newInstructions.addFirst
(Instruction.forOpcode(opc_pop)); //DCDAB
newInstructions.addFirst
(Instruction.forOpcode(opc_dup_x2)); //DCDABD
newInstructions.addFirst
(Instruction.forOpcode(opc_pop)); //DCABD
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x2)); //DCABDC
newInstructions.addFirst
(Instruction.forOpcode(opc_swap)); //ABDC
break;
}
}
if (count == 1) {
// Possible states:
// popped1 = 0; popped3 = 1;
// depth = 1 or depth = 2
if (depth == 1) {
newInstructions.addFirst
(Instruction.forOpcode(opc_swap));
} else {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop));
newInstructions.addFirst(instr);
}
break;
}
// count = 2; popped1 = 2
// Possible states:
// dpth/pop3 0 1
// 0 AB AAB AB
// 1 ABC BABC BAC
// 2 ABCD CABCD CABD
if (popped3 == 0) {
if (depth == 0) {
newInstructions.addFirst
(Instruction.forOpcode(opc_swap));
newInstructions.addFirst
(Instruction.forOpcode(opc_dup_x1));
newInstructions.addFirst
(Instruction.forOpcode(opc_swap));
} else if (depth == 1) {
newInstructions.addFirst
(Instruction.forOpcode(opc_swap)); //BABC
newInstructions.addFirst
(Instruction.forOpcode(opc_dup_x2)); //BACB
newInstructions.addFirst
(Instruction.forOpcode(opc_swap)); //ACB
} else {
newInstructions.addFirst
(Instruction.forOpcode(opc_swap)); //CABCD
newInstructions.addFirst
(Instruction.forOpcode(opc_pop2)); //CABDC
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x2)); //CABDCAB
newInstructions.addFirst
(Instruction.forOpcode(opc_pop)); //CDCAB
newInstructions.addFirst
(Instruction.forOpcode(opc_dup_x2)); //CDCABC
newInstructions.addFirst
(Instruction.forOpcode(opc_pop)); //CDABC
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x2)); //CDABCD
}
} else {
if (depth == 0) {
} else if (depth == 1) {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop)); //BAC
newInstructions.addFirst
(Instruction.forOpcode(opc_dup_x2)); //BACB
newInstructions.addFirst
(Instruction.forOpcode(opc_swap)); //ACB
} else {
newInstructions.addFirst
(Instruction.forOpcode(opc_pop2)); //CABD
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x1)); //CABDAB
newInstructions.addFirst
(Instruction.forOpcode(opc_pop2)); //CDAB
newInstructions.addFirst
(Instruction.forOpcode(opc_dup2_x2)); //CDABCD
}
}
break;
/* fall through */
case opc_checkcast:
if (isPop2) {
/* This is/may be a double pop on a single value
* split it and continue with second half
*/
iter.next();
iter.add(new Instruction(opc_pop));
iter.add(new Instruction(opc_pop));
iter.previous();
}
case opc_swap: {
// swap the popped status
boolean tmp = poppedEntries[stackDepth - 1];
poppedEntries[stackDepth - 1]
= poppedEntries[stackDepth - 2];
poppedEntries[stackDepth - 2] = tmp;
}
// Now the simple instructions, that can be removed.
// delta = -2;
case opc_ldc2_w:
case opc_lload: case opc_dload:
if (!push_all_popped)
throw new AssertError("pop half of a long");
poppedEntries[--stackDepth] = false;
poppedEntries[--stackDepth] = false;
continue;
case opc_i2l: case opc_i2d:
case opc_f2l: case opc_f2d:
case opc_ldc:
case opc_iload: case opc_fload: case opc_aload:
case opc_new:
case opc_lneg: case opc_dneg:
case opc_l2d: case opc_d2l:
case opc_laload: case opc_daload:
case opc_ineg: case opc_fneg:
case opc_i2f: case opc_f2i:
case opc_i2b: case opc_i2c: case opc_i2s:
case opc_newarray: case opc_anewarray:
case opc_arraylength:
case opc_instanceof:
case opc_lshl: case opc_lshr: case opc_lushr:
case opc_iaload: case opc_faload: case opc_aaload:
case opc_baload: case opc_caload: case opc_saload:
case opc_iadd: case opc_fadd:
case opc_isub: case opc_fsub:
case opc_imul: case opc_fmul:
case opc_idiv: case opc_fdiv:
case opc_irem: case opc_frem:
case opc_iand: case opc_ior : case opc_ixor:
case opc_ishl: case opc_ishr: case opc_iushr:
case opc_fcmpl: case opc_fcmpg:
case opc_l2i: case opc_l2f:
case opc_d2i: case opc_d2f:
case opc_ladd: case opc_dadd:
case opc_lsub: case opc_dsub:
case opc_lmul: case opc_dmul:
case opc_ldiv: case opc_ddiv:
case opc_lrem: case opc_drem:
case opc_land: case opc_lor : case opc_lxor:
case opc_lcmp:
case opc_dcmpl: case opc_dcmpg:
case opc_getstatic:
case opc_getfield:
case opc_multianewarray:
if (!push_all_popped)
throw new AssertError("pop half of a long");
if (poppush[0] < poppush[1]) {
for (int j=0; j < poppush[0] - poppush[1]; j++)
poppedEntries[stackDepth++] = true;
} else if (poppush[0] < poppush[1]) {
for (int j=0; j < poppush[0] - poppush[1]; j++)
poppedEntries[--stackDepth] = false;
}
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_checkcast:
if (!push_all_popped)
throw new AssertError("pop half of a long");
if (poppush[1] == 1) {
poppedEntries[--stackDepth] = false;
newInstructions
.addFirst(Instruction.forOpcode(opc_pop));
} else {
poppedEntries[--stackDepth] = false;
poppedEntries[--stackDepth] = false;
newInstructions
.addFirst(Instruction.forOpcode(opc_pop2));
}
newInstructions.addFirst(instr);
default:
throw new AssertError("Unexpected opcode!");
}
} else {
// Add the instruction ..
newInstructions.addFirst(instr);
// .. and adjust stackDepth.
stackDepth += poppush[0] - poppush[1];
}
// append the pop behind the unresolvable opcode.
iter.next();
iter.add(popInstr);
continue;
}
for (int j=0; j < stackDepth; j++) {
// XXXX
}
blocks[i].setCode((Instruction[]) newInstructions
.toArray(oldInstrs), blocks[i].getSuccs());
}
}
}

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

@ -27,22 +27,38 @@ import java.io.IOException;
*
* @author Jochen Hoenicke
*/
public class ArrayType extends ReferenceType {
// The interfaces that an array implements:
final static ClassInfo[] arrayIfaces = {
// Make sure to list all interfaces, even if some interface
// implements another (or change code in getGeneralizedType().
ClassInfo.forName("java.lang.Cloneable"),
ClassInfo.forName("java.io.Serializable")
};
public class ArrayType extends ClassType {
Type elementType;
ArrayType(Type elementType) {
super(TC_ARRAY);
super(TC_ARRAY, elementType + "[]");
this.elementType = elementType;
}
public boolean isInterface() {
return false;
}
public boolean isUnknown() {
if (elementType instanceof ClassType)
return ((ClassType) elementType).isUnknown();
return false;
}
public boolean isFinal() {
if (elementType instanceof ClassType)
return ((ClassType) elementType).isFinal();
return false;
}
public ClassType getSuperClass() {
return tObject;
}
public ClassType[] getInterfaces() {
return arrayIfaces;
}
public Type getElementType() {
return elementType;
}
@ -64,6 +80,14 @@ public class ArrayType extends ReferenceType {
return tArray(elementType.getCanonic());
}
public boolean isSubTypeOf(Type type) {
if (type == tNull)
return true;
if (type instanceof ArrayType)
return elementType.isSubTypeOf(((ArrayType) type).elementType);
return false;
}
/**
* Create the type corresponding to the range from bottomType to this.
* @param bottomType the start point of the range
@ -75,22 +99,11 @@ public class ArrayType extends ReferenceType {
* obj , tArray(x) -> <obj, tArray(x)>
* iff tArray extends and implements obj
*/
if (bottom.getTypeCode() == TC_ARRAY)
if (bottom instanceof ArrayType)
return tArray(elementType.intersection
(((ArrayType)bottom).elementType));
if (bottom.getTypeCode() == TC_CLASS) {
ClassInterfacesType bottomCIT = (ClassInterfacesType)bottom;
// int len = arrayIfacesStrs.length;
// ClassInfo[] arrayIfaces = new ClassInfo[len];
// for (int i=0; i< arrayIfacesStrs.length; i++)
// arrayIfaces[i]
// = bottomCIT.classPath.getClassInfo(arrayIfacesStrs[i]);
if (bottomCIT.clazz == null
&& implementsAllIfaces(null, arrayIfaces, bottomCIT.ifaces))
return tRange(bottomCIT, this);
}
return tError;
return super.createRangeType(bottom);
}
/**
@ -100,9 +113,8 @@ public class ArrayType extends ReferenceType {
*/
public Type getSpecializedType(Type type) {
/*
* tArray(x), object -> tArray(x) iff tArray implements object
* tArray(x), tArray(y) -> tArray(x.intersection(y))
* tArray(x), other -> tError
* tArray(x), other -> tArray(x) iff tArray implements object
*/
if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getBottom();
@ -113,12 +125,8 @@ public class ArrayType extends ReferenceType {
return tArray(elementType.intersection
(((ArrayType)type).elementType));
}
if (type.getTypeCode() == TC_CLASS) {
ClassInterfacesType other = (ClassInterfacesType) type;
if (other.clazz == null
&& implementsAllIfaces(null, arrayIfaces, other.ifaces))
return this;
}
if (type.isSubTypeOf(this))
return this;
return tError;
}
@ -138,78 +146,14 @@ public class ArrayType extends ReferenceType {
}
if (type == tNull)
return this;
if (type.getTypeCode() == TC_ARRAY)
if (type instanceof ArrayType)
return tArray(elementType.intersection
(((ArrayType)type).elementType));
if (type.getTypeCode() == TC_CLASS) {
ClassInterfacesType other = (ClassInterfacesType) type;
if (implementsAllIfaces(other.clazz, other.ifaces, arrayIfaces))
return ClassInterfacesType.create(null, arrayIfaces);
if (other.clazz == null
&& implementsAllIfaces(null, arrayIfaces, other.ifaces))
return other;
/* Now the more complicated part: find all interfaces, that are
* implemented by one interface or class in each group.
*
* First get all interfaces of this.clazz and this.ifaces.
*/
Vector newIfaces = new Vector();
try {
iface_loop:
for (int i=0; i < arrayIfaces.length; i++) {
/* Now consider each array interface. If any clazz or
* interface in other implements it, add it to the
* newIfaces vector. */
if (other.clazz != null
&& arrayIfaces[i].implementedBy(other.clazz)) {
newIfaces.addElement(arrayIfaces[i]);
continue iface_loop;
}
for (int j=0; j<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;
}
/**
* 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;
if (!(type instanceof ReferenceType))
return tError;
return ((ReferenceType)type).getGeneralizedType(this);
}
/**
@ -220,20 +164,12 @@ public class ArrayType extends ReferenceType {
return elementType.isValidType();
}
public boolean isClassType() {
return true;
}
public String getTypeSignature() {
return "["+elementType.getTypeSignature();
}
public Class getTypeClass() throws ClassNotFoundException {
return Class.forName("["+elementType.getTypeSignature());
}
public String toString() {
return elementType.toString()+"[]";
return Class.forName(getTypeSignature());
}
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;
}
/**
* 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.
* @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 = \
ArrayType.java \
ClassInterfacesType.java \
ClassType.java \
ClassInfoType.java \
IntegerType.java \
MethodType.java \
MultiClassType.java \
NullType.java \
RangeType.java \
ReferenceType.java \
SystemClassType.java \
Type.java
noinst_DATA = $(MY_JAVA_FILES:.java=.class)

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

@ -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;
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
@ -53,7 +57,9 @@ public class NullType extends ReferenceType {
public Type getGeneralizedType(Type type) {
if (type.typecode == TC_RANGE)
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) {
if (type.typecode == TC_RANGE)
type = ((RangeType) type).getBottom();
return type;
if (type != tNull)
return tError;
return tNull;
}
public String toString() {
return "tNull";
}
public Type findCommonClassTypes(Stack otherTypes) {
throw new UnsupportedOperationException();
}
/**
* Intersect this type with another type and return the new type.
* @param type the other type.

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

@ -21,10 +21,14 @@ package jode.type;
import jode.GlobalOptions;
import jode.bytecode.ClassInfo;
import java.io.IOException;
import java.util.Stack;
import java.util.Vector;
import java.util.Enumeration;
/**
* This is an abstrace super class of all reference types. Reference
* types are ClassInterfacesType, ArrayType and NullType. <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
* more operations: specialization, generalization and
@ -56,6 +60,7 @@ public abstract class ReferenceType extends Type {
* @param type the other type.
* @return the specialized type. */
public abstract Type getSpecializedType(Type type);
/**
* Returns the generalized type set of this and type. The result
* should be a type set, so that every type, is extended/implemented
@ -65,6 +70,49 @@ public abstract class ReferenceType extends Type {
* @return the generalized type
*/
public abstract Type getGeneralizedType(Type type);
public Type findCommonClassTypes(Stack otherTypes) {
/* Consider each class and interface implemented by this.
* If any clazz or interface in other implements it, add it to
* the classes vector. Otherwise consider all sub interfaces.
*/
Vector classes = new Vector();
type_loop:
while (!otherTypes.isEmpty()) {
ClassType type = (ClassType) otherTypes.pop();
if (type.equals(tObject))
/* tObject is always implied. */
continue type_loop;
for (Enumeration enum = classes.elements();
enum.hasMoreElements(); ) {
if (type.isSubTypeOf((Type) enum.nextElement()))
/* We can skip this, as another class already
* implies it. */
continue type_loop;
}
if (type.isSubTypeOf(this)) {
classes.addElement(type);
continue type_loop;
}
/* This clazz/interface is not implemented by this object.
* Try its parents now.
*/
ClassType ifaces[] = type.getInterfaces();
for (int i=0; i < ifaces.length; i++)
otherTypes.push(ifaces[i]);
ClassType superClass = type.getSuperClass();
if (superClass != null)
otherTypes.push(superClass);
}
ClassType[] classArray = new ClassType[classes.size()];
classes.copyInto(classArray);
return MultiClassType.create(classArray);
}
/**
* Creates a range type set of this and bottom. The resulting type set
* contains all types, that extend all types in bottom and are extended

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

Loading…
Cancel
Save