ComplexExpression removed, complete rework, StoreInstruction/LValue splittet.

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@755 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 25 years ago
parent 3840a9ddde
commit 6b7ac0945c
  1. 20
      jode/jode/expr/ArrayLengthOperator.java
  2. 41
      jode/jode/expr/ArrayLoadOperator.java
  3. 47
      jode/jode/expr/ArrayStoreOperator.java
  4. 45
      jode/jode/expr/BinaryOperator.java
  5. 20
      jode/jode/expr/CheckCastOperator.java
  6. 40
      jode/jode/expr/CheckNullOperator.java
  7. 3
      jode/jode/expr/ClassFieldOperator.java
  8. 10
      jode/jode/expr/CombineableOperator.java
  9. 50
      jode/jode/expr/CompareBinaryOperator.java
  10. 34
      jode/jode/expr/CompareToIntOperator.java
  11. 74
      jode/jode/expr/CompareUnaryOperator.java
  12. 26
      jode/jode/expr/ConstOperator.java
  13. 55
      jode/jode/expr/ConstantArrayOperator.java
  14. 75
      jode/jode/expr/ConstructorOperator.java
  15. 21
      jode/jode/expr/ConvertOperator.java
  16. 146
      jode/jode/expr/Expression.java
  17. 65
      jode/jode/expr/GetFieldOperator.java
  18. 73
      jode/jode/expr/IIncOperator.java
  19. 93
      jode/jode/expr/IfThenElseOperator.java
  20. 36
      jode/jode/expr/InstanceOfOperator.java
  21. 353
      jode/jode/expr/InvokeOperator.java
  22. 31
      jode/jode/expr/LValueExpression.java
  23. 49
      jode/jode/expr/LocalLoadOperator.java
  24. 48
      jode/jode/expr/LocalStoreOperator.java
  25. 2
      jode/jode/expr/LocalVarOperator.java
  26. 18
      jode/jode/expr/MonitorEnterOperator.java
  27. 18
      jode/jode/expr/MonitorExitOperator.java
  28. 27
      jode/jode/expr/NewArrayOperator.java
  29. 4
      jode/jode/expr/NewOperator.java
  30. 16
      jode/jode/expr/NoArgOperator.java
  31. 42
      jode/jode/expr/NopOperator.java
  32. 286
      jode/jode/expr/Operator.java
  33. 20
      jode/jode/expr/PopOperator.java
  34. 60
      jode/jode/expr/PrePostFixOperator.java
  35. 44
      jode/jode/expr/PutFieldOperator.java
  36. 9
      jode/jode/expr/ShiftOperator.java
  37. 20
      jode/jode/expr/SimpleOperator.java
  38. 129
      jode/jode/expr/StoreInstruction.java
  39. 33
      jode/jode/expr/StringAddOperator.java
  40. 10
      jode/jode/expr/ThisOperator.java
  41. 39
      jode/jode/expr/UnaryOperator.java

@ -23,33 +23,25 @@ import jode.decompiler.TabbedPrintWriter;
public class ArrayLengthOperator extends Operator { public class ArrayLengthOperator extends Operator {
Type arrayType;
public ArrayLengthOperator() { public ArrayLengthOperator() {
super(Type.tInt, 0); super(Type.tInt, 0);
arrayType = Type.tArray(Type.tUnknown); initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 950; return 950;
} }
public int getOperandCount() { public void updateSubTypes() {
return 1; subExpressions[0].setType(Type.tArray(Type.tUnknown));
}
public Type getOperandType(int i) {
return arrayType;
} }
public void setOperandType(Type[] types) { public void updateType() {
arrayType = arrayType.intersection(types[0]);
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 900); subExpressions[0].dumpExpression(writer, 900);
writer.print(".length"); writer.print(".length");
} }
} }

@ -22,46 +22,37 @@ import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class ArrayLoadOperator extends SimpleOperator { public class ArrayLoadOperator extends Operator {
String value; String value;
public ArrayLoadOperator(Type type) { public ArrayLoadOperator(Type type) {
super(type, 0, 2); super(type, 0);
operandTypes[0] = Type.tArray(type); initOperands(2);
operandTypes[1] = Type.tInt;
} }
public int getPriority() { public int getPriority() {
return 950; return 950;
} }
/** public void updateSubTypes() {
* Sets the return type of this operator. subExpressions[0].setType(Type.tSubType(Type.tArray(type)));
*/ subExpressions[1].setType(Type.tSubType(Type.tInt));
public void setType(Type type) {
if (!type.equals(this.type)) {
super.setType(type);
operandTypes[0] = Type.tArray(type);
}
} }
public void setOperandType(Type[] t) { public void updateType() {
super.setOperandType(t); Type subType = Type.tSuperType(subExpressions[0].getType())
if (operandTypes[0] == Type.tError) .intersection(Type.tArray(type));
type = Type.tError; if (!(subType instanceof ArrayType))
else if (operandTypes[0] instanceof ArrayType) updateParentType(Type.tError);
type = type.intersection else
(((ArrayType)operandTypes[0]).getElementType()); updateParentType(((ArrayType)subType).getElementType());
else
throw new jode.AssertError("No Array type: "+operandTypes[0]);
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 950); subExpressions[0].dumpExpression(writer, 950);
writer.print("["); writer.print("[");
operands[1].dumpExpression(writer, 0); subExpressions[1].dumpExpression(writer, 0);
writer.print("]"); writer.print("]");
} }
} }

@ -22,53 +22,40 @@ import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class ArrayStoreOperator extends StoreInstruction { public class ArrayStoreOperator extends LValueExpression {
Type indexType;
public ArrayStoreOperator(Type type, int operator) {
super(type, operator);
indexType = Type.tInt;
}
public ArrayStoreOperator(Type type) { public ArrayStoreOperator(Type type) {
this(type, ASSIGN_OP); super(type);
initOperands(2);
} }
public boolean matches(Operator loadop) { public boolean matches(Operator loadop) {
return loadop instanceof ArrayLoadOperator; return loadop instanceof ArrayLoadOperator;
} }
public int getLValuePriority() { public int getPriority() {
return 950; return 950;
} }
public int getLValueOperandCount() { public void updateSubTypes() {
return 2; subExpressions[0].setType(Type.tArray(type));
} subExpressions[1].setType(Type.tSubType(Type.tInt));
public Type getLValueOperandType(int i) {
if (i == 0)
return Type.tArray(lvalueType);
else
return indexType;
} }
public void setLValueOperandType(Type[] t) { public void updateType() {
indexType = indexType.intersection(t[1]); Type subType = subExpressions[0].getType()
Type arrayType = t[0].intersection(Type.tArray(lvalueType)); .intersection(Type.tArray(type));
if (arrayType == Type.tError) if (!(subType instanceof ArrayType))
lvalueType = Type.tError; updateParentType(Type.tError);
else else
lvalueType = ((ArrayType)arrayType).getElementType(); updateParentType(((ArrayType)subType).getElementType());
} }
public void dumpLValue(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 950); subExpressions[0].dumpExpression(writer, 950);
writer.print("["); writer.print("[");
operands[1].dumpExpression(writer, 0); subExpressions[1].dumpExpression(writer, 0);
writer.print("]"); writer.print("]");
} }
} }

@ -21,14 +21,15 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class BinaryOperator extends SimpleOperator { public class BinaryOperator extends Operator {
public BinaryOperator(Type type, int op) { public BinaryOperator(Type type, int op) {
super(type, op, 2); super(type, op);
initOperands(2);
} }
public int getPriority() { public int getPriority() {
switch (operator) { switch (operatorIndex) {
case 1: case 2: case 1: case 2:
return 610; return 610;
case 3: case 4: case 5: case 3: case 4: case 5:
@ -52,28 +53,42 @@ public class BinaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator"); throw new RuntimeException("Illegal operator");
} }
public int getOperandPriority(int i) { public void updateSubTypes() {
return getPriority() + i; subExpressions[0].setType(Type.tSubType(type));
subExpressions[1].setType(Type.tSubType(type));
} }
public Type getOperandType(int i) { public void updateType() {
return type; Type leftType = Type.tSuperType(subExpressions[0].getType());
Type rightType = Type.tSuperType(subExpressions[1].getType());
subExpressions[0].setType(Type.tSubType(rightType));
subExpressions[1].setType(Type.tSubType(leftType));
/* propagate hints? XXX */
updateParentType(leftType.intersection(rightType));
} }
public void setOperandType(Type[] inputTypes) { public Expression negate() {
setType(inputTypes[0].intersection(inputTypes[1])); if (getOperatorIndex() == LOG_AND_OP ||
getOperatorIndex() == LOG_OR_OP) {
setOperatorIndex(getOperatorIndex() ^ 1);
for (int i=0; i< 2; i++) {
subExpressions[i] = subExpressions[i].negate();
subExpressions[i].parent = this;
}
return this;
}
return super.negate();
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return (o instanceof BinaryOperator) && return (o instanceof BinaryOperator) &&
((BinaryOperator)o).operator == operator; o.operatorIndex == operatorIndex;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, getPriority()); subExpressions[0].dumpExpression(writer, getPriority());
writer.print(getOperatorString()); writer.print(getOperatorString());
operands[1].dumpExpression(writer, getPriority()+1); subExpressions[1].dumpExpression(writer, getPriority()+1);
} }
} }

@ -21,25 +21,27 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class CheckCastOperator extends SimpleOperator { public class CheckCastOperator extends Operator {
Type castType; Type castType;
public CheckCastOperator(Type type) { public CheckCastOperator(Type type) {
super(type, 0, 1); super(type, 0);
castType = type; castType = type;
operandTypes[0] = Type.tUnknown; initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 700; return 700;
} }
public void setOperandType(Type[] type) { public void updateSubTypes() {
super.setOperandType(type); subExpressions[0].setType(Type.tUObject);
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateType() {
Expression[] operands) }
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
writer.print("("); writer.print("(");
writer.printType(castType); writer.printType(castType);
@ -49,12 +51,12 @@ public class CheckCastOperator extends SimpleOperator {
* to the common super type before. This cases always give a runtime * to the common super type before. This cases always give a runtime
* error, but we want to decompile even bad programs. * error, but we want to decompile even bad programs.
*/ */
Type superType = castType.getCastHelper(operands[0].getType()); Type superType = castType.getCastHelper(subExpressions[0].getType());
if (superType != null) { if (superType != null) {
writer.print("("); writer.print("(");
writer.printType(superType); writer.printType(superType);
writer.print(") "); writer.print(") ");
} }
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

@ -40,56 +40,38 @@ import jode.decompiler.TabbedPrintWriter;
*/ */
public class CheckNullOperator extends Operator { public class CheckNullOperator extends Operator {
Type operandType;
LocalInfo local; LocalInfo local;
public CheckNullOperator(Type type, LocalInfo li) { public CheckNullOperator(Type type, LocalInfo li) {
super(type, 0); super(type, 0);
operandType = type;
local = li; local = li;
local.setType(type); initOperands(1);
}
public int getOperandCount() {
return 1;
} }
public int getPriority() { public int getPriority() {
return 200; return 200;
} }
public int getOperandPriority(int i) { public void updateSubTypes() {
return 0; local.setType(type);
} subExpressions[0].setType(Type.tSubType(type));
public Type getOperandType(int i) {
return operandType;
} }
public void setOperandType(Type[] inputTypes) { public void updateType() {
operandType = operandType.intersection(inputTypes[0]); Type newType = Type.tSuperType(subExpressions[0].getType())
type = operandType; .intersection(type);
local.setType(type); local.setType(newType);
updateParentType(newType);
} }
public void removeLocal() { public void removeLocal() {
local.remove(); local.remove();
} }
/** public void dumpExpression(TabbedPrintWriter writer)
* Sets the return type of this operator.
*/
public void setType(Type newType) {
type = operandType = operandType.intersection(newType);
local.setType(type);
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
writer.print("("+local.getName()+" = "); writer.print("("+local.getName()+" = ");
operands[0].dumpExpression(writer, 0); subExpressions[0].dumpExpression(writer, 0);
writer.print(").getClass() != null ? "+local.getName()+" : null"); writer.print(").getClass() != null ? "+local.getName()+" : null");
} }
} }

@ -34,8 +34,7 @@ public class ClassFieldOperator extends NoArgOperator {
return 950; return 950;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
writer.printType(classType); writer.printType(classType);
writer.print(".class"); writer.print(".class");

@ -19,7 +19,15 @@
package jode.expr; package jode.expr;
public interface CombineableOperator extends MatchableOperator { public interface CombineableOperator {
/**
* Returns the LValue.
*/
public LValueExpression getLValue();
/**
* Checks if the loadOp is combinable with the lvalue.
*/
public boolean lvalueMatches(Operator loadOp);
/** /**
* Make this operator return a value compatible with the loadOp * Make this operator return a value compatible with the loadOp
* that it should replace. * that it should replace.

@ -21,10 +21,13 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class CompareBinaryOperator extends SimpleOperator { public class CompareBinaryOperator extends Operator {
Type compareType;
public CompareBinaryOperator(Type type, int op) { public CompareBinaryOperator(Type type, int op) {
super(Type.tBoolean, op, 2); super(Type.tBoolean, op);
operandTypes[0] = operandTypes[1] = type; compareType = type;
initOperands(2);
} }
public int getPriority() { public int getPriority() {
@ -41,22 +44,41 @@ public class CompareBinaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator"); throw new RuntimeException("Illegal operator");
} }
public void setOperandType(Type[] inputTypes) { public Type getCompareType() {
super.setOperandType(inputTypes); return compareType;
operandTypes[0] = operandTypes[1] = }
operandTypes[0].intersection(operandTypes[1]);
public void updateSubTypes() {
subExpressions[0].setType(Type.tSubType(compareType));
subExpressions[1].setType(Type.tSubType(compareType));
}
public void updateType() {
Type leftType = Type.tSuperType(subExpressions[0].getType());
Type rightType = Type.tSuperType(subExpressions[1].getType());
subExpressions[0].setType(Type.tSubType(rightType));
subExpressions[1].setType(Type.tSubType(leftType));
/* propagate hints? XXX */
}
public Expression negate() {
if ((getType() != Type.tFloat && getType() != Type.tDouble)
|| getOperatorIndex() <= NOTEQUALS_OP) {
setOperatorIndex(getOperatorIndex() ^ 1);
return this;
}
return super.negate();
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return (o instanceof CompareBinaryOperator) && return (o instanceof CompareBinaryOperator)
((CompareBinaryOperator)o).operator == operator; && o.operatorIndex == operatorIndex;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, getPriority()); subExpressions[0].dumpExpression(writer, getPriority());
writer.print(getOperatorString()); writer.print(getOperatorString());
operands[1].dumpExpression(writer, getPriority()+1); subExpressions[1].dumpExpression(writer, getPriority()+1);
} }
} }

@ -21,35 +21,43 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class CompareToIntOperator extends SimpleOperator { public class CompareToIntOperator extends Operator {
boolean allowsNAN;
boolean greaterOnNAN; boolean greaterOnNAN;
Type compareType;
public CompareToIntOperator(Type type, boolean greaterOnNAN) { public CompareToIntOperator(Type type, boolean greaterOnNAN) {
super(Type.tInt, 0, 2); super(Type.tInt, 0);
operandTypes[0] = operandTypes[1] = type; compareType = type;
this.allowsNAN = (type == Type.tFloat || type == Type.tDouble);
this.greaterOnNAN = greaterOnNAN; this.greaterOnNAN = greaterOnNAN;
initOperands(2);
} }
public int getPriority() { public int getPriority() {
return 499; return 499;
} }
public void setOperandType(Type[] inputTypes) { public void updateSubTypes() {
super.setOperandType(inputTypes); subExpressions[0].setType(Type.tSubType(compareType));
Type operandType = operandTypes[0].intersection(operandTypes[1]); subExpressions[1].setType(Type.tSubType(compareType));
operandTypes[0] = operandTypes[1] = operandType;
} }
public boolean equals(Object o) { public void updateType() {
}
public boolean opEquals(Operator o) {
return (o instanceof CompareToIntOperator); return (o instanceof CompareToIntOperator);
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException throws java.io.IOException
{ {
operands[0].dumpExpression(writer, 550); subExpressions[0].dumpExpression(writer, 550);
writer.print(" <=>" + (greaterOnNAN ? 'g' : 'l') + ' '); writer.print(" <=>");
operands[1].dumpExpression(writer, 551); if (allowsNAN)
writer.print(greaterOnNAN ? "g" : "l");
writer.print(" ");
subExpressions[1].dumpExpression(writer, 551);
} }
} }

@ -21,13 +21,15 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class CompareUnaryOperator extends SimpleOperator { public class CompareUnaryOperator extends Operator {
boolean objectType; boolean objectType;
Type compareType;
public CompareUnaryOperator(Type type, int op) { public CompareUnaryOperator(Type type, int op) {
super(Type.tBoolean, op, 1); super(Type.tBoolean, op);
operandTypes[0] = type; compareType = type;
objectType = (type.isOfType(Type.tUObject)); objectType = (type.isOfType(Type.tUObject));
initOperands(1);
} }
public int getPriority() { public int getPriority() {
@ -44,15 +46,69 @@ public class CompareUnaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator"); throw new RuntimeException("Illegal operator");
} }
public boolean equals(Object o) { public Type getCompareType() {
return (o instanceof CompareUnaryOperator) && return compareType;
((CompareUnaryOperator)o).operator == operator;
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateSubTypes() {
Expression[] operands) subExpressions[0].setType(Type.tSubType(compareType));
}
public void updateType() {
}
public Expression simplify() {
if (subExpressions[0] instanceof CompareToIntOperator) {
CompareToIntOperator cmpOp
= (CompareToIntOperator) subExpressions[0];
boolean negated = false;
int opIndex = getOperatorIndex();
if (cmpOp.allowsNAN && getOperatorIndex() > NOTEQUALS_OP) {
if (cmpOp.greaterOnNAN ==
(opIndex == GREATEREQ_OP || opIndex == GREATER_OP)) {
negated = true;
opIndex ^= 1;
}
}
Expression newOp = new CompareBinaryOperator
(cmpOp.compareType, opIndex)
.addOperand(cmpOp.subExpressions[1])
.addOperand(cmpOp.subExpressions[0]);
if (negated)
return newOp.negate().simplify();
return newOp.simplify();
}
if (subExpressions[0].getType().isOfType(Type.tBoolean)) {
/* xx == false */
if (getOperatorIndex() == EQUALS_OP)
return subExpressions[0].negate().simplify();
/* xx != false */
if (getOperatorIndex() == NOTEQUALS_OP)
return subExpressions[0].simplify();
}
return super.simplify();
}
public Expression negate() {
if ((getType() != Type.tFloat && getType() != Type.tDouble)
|| getOperatorIndex() <= NOTEQUALS_OP) {
setOperatorIndex(getOperatorIndex() ^ 1);
return this;
}
return super.negate();
}
public boolean opEquals(Operator o) {
return (o instanceof CompareUnaryOperator)
&& o.getOperatorIndex() == getOperatorIndex();
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, getPriority()); subExpressions[0].dumpExpression(writer, getPriority());
writer.print(getOperatorString()); writer.print(getOperatorString());
writer.print(objectType?"null":"0"); writer.print(objectType?"null":"0");
} }

@ -34,11 +34,11 @@ public class ConstOperator extends NoArgOperator {
public ConstOperator(Object constant) { public ConstOperator(Object constant) {
super(Type.tUnknown); super(Type.tUnknown);
if (constant instanceof Boolean) { if (constant instanceof Boolean) {
setType(Type.tBoolean); updateParentType(Type.tBoolean);
constant = new Integer(((Boolean)constant).booleanValue() ? 1 : 0); constant = new Integer(((Boolean)constant).booleanValue() ? 1 : 0);
} else if (constant instanceof Integer) { } else if (constant instanceof Integer) {
int intVal = ((Integer) constant).intValue(); int intVal = ((Integer) constant).intValue();
setType updateParentType
((intVal == 0 || intVal == 1) ? tBoolConstInt ((intVal == 0 || intVal == 1) ? tBoolConstInt
: (intVal < Short.MIN_VALUE : (intVal < Short.MIN_VALUE
|| intVal > Character.MAX_VALUE) ? Type.tInt || intVal > Character.MAX_VALUE) ? Type.tInt
@ -54,15 +54,15 @@ public class ConstOperator extends NoArgOperator {
? IntegerType.IT_S|IntegerType.IT_C|IntegerType.IT_I ? IntegerType.IT_S|IntegerType.IT_C|IntegerType.IT_I
: IntegerType.IT_C|IntegerType.IT_I)); : IntegerType.IT_C|IntegerType.IT_I));
} else if (constant instanceof Long) } else if (constant instanceof Long)
setType(Type.tLong); updateParentType(Type.tLong);
else if (constant instanceof Float) else if (constant instanceof Float)
setType(Type.tFloat); updateParentType(Type.tFloat);
else if (constant instanceof Double) else if (constant instanceof Double)
setType(Type.tDouble); updateParentType(Type.tDouble);
else if (constant instanceof String) else if (constant instanceof String)
setType(Type.tString); updateParentType(Type.tString);
else if (constant == null) else if (constant == null)
setType(Type.tUObject); updateParentType(Type.tUObject);
else else
throw new IllegalArgumentException("Illegal constant type: " throw new IllegalArgumentException("Illegal constant type: "
+constant.getClass()); +constant.getClass());
@ -77,7 +77,7 @@ public class ConstOperator extends NoArgOperator {
return 1000; return 1000;
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
if (o instanceof ConstOperator) { if (o instanceof ConstOperator) {
Object otherValue = ((ConstOperator)o).value; Object otherValue = ((ConstOperator)o).value;
return value == null return value == null
@ -173,7 +173,7 @@ public class ConstOperator extends NoArgOperator {
} else if (type.equals(Type.tString)) { } else if (type.equals(Type.tString)) {
return quoted(strVal); return quoted(strVal);
} else if (parent != null) { } else if (parent != null) {
int opindex = parent.getOperator().getOperatorIndex(); int opindex = parent.getOperatorIndex();
if (opindex >= OPASSIGN_OP + ADD_OP if (opindex >= OPASSIGN_OP + ADD_OP
&& opindex < OPASSIGN_OP + ASSIGN_OP) && opindex < OPASSIGN_OP + ASSIGN_OP)
opindex -= OPASSIGN_OP; opindex -= OPASSIGN_OP;
@ -204,8 +204,9 @@ public class ConstOperator extends NoArgOperator {
&& (type.getHint().equals(Type.tByte) && (type.getHint().equals(Type.tByte)
|| type.getHint().equals(Type.tShort)) || type.getHint().equals(Type.tShort))
&& !isInitializer && !isInitializer
&& (parent == null && !(parent instanceof StoreInstruction
|| parent.getOperator().getOperatorIndex() != ASSIGN_OP)) { && parent.getOperatorIndex() != ASSIGN_OP
&& parent.subExpressions[1] == this)) {
/* One of the strange things in java. All constants /* One of the strange things in java. All constants
* are int and must be explicitly casted to byte,...,short. * are int and must be explicitly casted to byte,...,short.
* But in assignments and initializers this cast is unnecessary. * But in assignments and initializers this cast is unnecessary.
@ -217,8 +218,7 @@ public class ConstOperator extends NoArgOperator {
return strVal; return strVal;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
writer.print(toString()); writer.print(toString());
} }

@ -22,18 +22,16 @@ import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class ConstantArrayOperator extends NoArgOperator { public class ConstantArrayOperator extends Operator {
boolean isInitializer;
ConstOperator empty; ConstOperator empty;
Expression[] values;
Type argType; Type argType;
boolean isInitializer;
public ConstantArrayOperator(Type type, int size) { public ConstantArrayOperator(Type type, int size) {
super(type); super(type);
values = new Expression[size];
argType = (type instanceof ArrayType) argType = (type instanceof ArrayType)
? Type.tSubType(((ArrayType)type).getElementType()) : Type.tError; ? Type.tSubType(((ArrayType)type).getElementType()) : Type.tError;
Object emptyVal; Object emptyVal;
if (argType == type.tError || argType.isOfType(Type.tUObject)) if (argType == type.tError || argType.isOfType(Type.tUObject))
emptyVal = null; emptyVal = null;
@ -48,31 +46,33 @@ public class ConstantArrayOperator extends NoArgOperator {
else else
throw new IllegalArgumentException("Illegal Type: "+argType); throw new IllegalArgumentException("Illegal Type: "+argType);
empty = new ConstOperator(emptyVal); empty = new ConstOperator(emptyVal);
empty.setType(argType); empty.setType(argType);
empty.makeInitializer(); empty.makeInitializer();
initOperands(size);
for (int i=0; i < subExpressions.length; i++)
setSubExpressions(i, empty);
} }
public void setType(Type newtype) { public void updateSubTypes() {
super.setType(newtype); argType = (type instanceof ArrayType)
Type newArgType = (this.type instanceof ArrayType) ? Type.tSubType(((ArrayType)type).getElementType()) : Type.tError;
? Type.tSubType(((ArrayType)this.type).getElementType()) for (int i=0; i< subExpressions.length; i++)
: Type.tError; if (subExpressions[i] != null)
if (!newArgType.equals(argType)) { subExpressions[i].setType(argType);
argType = newArgType; }
empty.setType(argType);
for (int i=0; i< values.length; i++) public void updateType() {
if (values[i] != null)
values[i].setType(argType);
}
} }
public boolean setValue(int index, Expression value) { public boolean setValue(int index, Expression value) {
if (index < 0 || index > values.length || values[index] != null) if (index < 0 ||
index > subExpressions.length ||
subExpressions[index] != empty)
return false; return false;
value.setType(argType); value.setType(argType);
setType(Type.tSuperType(Type.tArray(value.getType()))); setType(Type.tSuperType(Type.tArray(value.getType())));
values[index] = value; subExpressions[index] = value;
value.parent = this; value.parent = this;
value.makeInitializer(); value.makeInitializer();
return true; return true;
@ -87,15 +87,14 @@ public class ConstantArrayOperator extends NoArgOperator {
} }
public Expression simplify() { public Expression simplify() {
for (int i=0; i< values.length; i++) { for (int i=0; i< subExpressions.length; i++) {
if (values[i] != null) if (subExpressions[i] != null)
values[i] = values[i].simplify(); subExpressions[i] = subExpressions[i].simplify();
} }
return this; return this;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
if (!isInitializer) { if (!isInitializer) {
writer.print("new "); writer.print("new ");
@ -104,15 +103,15 @@ public class ConstantArrayOperator extends NoArgOperator {
} }
writer.openBraceNoSpace(); writer.openBraceNoSpace();
writer.tab(); writer.tab();
for (int i=0; i< values.length; i++) { for (int i=0; i< subExpressions.length; i++) {
if (i>0) { if (i>0) {
if (i % 10 == 0) if (i % 10 == 0)
writer.println(","); writer.println(",");
else else
writer.print(", "); writer.print(", ");
} }
if (values[i] != null) if (subExpressions[i] != null)
values[i].dumpExpression(writer, 0); subExpressions[i].dumpExpression(writer, 0);
else else
empty.dumpExpression(writer, 0); empty.dumpExpression(writer, 0);
} }

@ -18,12 +18,15 @@
*/ */
package jode.expr; package jode.expr;
import java.lang.reflect.Modifier;
import jode.type.Type; import jode.type.Type;
import jode.type.NullType; import jode.type.NullType;
import jode.type.ClassInterfacesType; import jode.type.ClassInterfacesType;
import jode.type.MethodType; import jode.type.MethodType;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo; import jode.bytecode.InnerClassInfo;
import jode.bytecode.Reference;
import jode.Decompiler; import jode.Decompiler;
import jode.decompiler.CodeAnalyzer; import jode.decompiler.CodeAnalyzer;
import jode.decompiler.ClassAnalyzer; import jode.decompiler.ClassAnalyzer;
@ -38,11 +41,22 @@ public class ConstructorOperator extends Operator
ClassAnalyzer anonymousClass = null; ClassAnalyzer anonymousClass = null;
boolean removedCheckNull = false; boolean removedCheckNull = false;
public ConstructorOperator(Reference ref, CodeAnalyzer codeAna,
boolean isVoid) {
super(isVoid ? Type.tVoid : Type.tType(ref.getClazz()), 0);
this.classType = Type.tType(ref.getClazz());
this.methodType = Type.tMethod(ref.getType());
this.codeAnalyzer = codeAna;
initOperands(methodType.getParameterTypes().length);
checkAnonymousClasses();
}
public ConstructorOperator(InvokeOperator invoke, boolean isVoid) { public ConstructorOperator(InvokeOperator invoke, boolean isVoid) {
super(isVoid ? Type.tVoid : invoke.getClassType(), 0); super(isVoid ? Type.tVoid : invoke.getClassType(), 0);
this.classType = invoke.getClassType(); this.classType = invoke.getClassType();
this.methodType = invoke.getMethodType(); this.methodType = invoke.getMethodType();
this.codeAnalyzer = invoke.codeAnalyzer; this.codeAnalyzer = invoke.codeAnalyzer;
initOperands(methodType.getParameterTypes().length);
checkAnonymousClasses(); checkAnonymousClasses();
} }
@ -54,16 +68,6 @@ public class ConstructorOperator extends Operator
return codeAnalyzer; return codeAnalyzer;
} }
/**
* Checks if the value of the given expression can change, due to
* side effects in this expression. If this returns false, the
* expression can safely be moved behind the current expresion.
* @param expr the expression that should not change.
*/
public boolean hasSideEffects(Expression expr) {
return expr.containsConflictingLoad(this);
}
/** /**
* Checks if the value of the operator can be changed by this expression. * Checks if the value of the operator can be changed by this expression.
*/ */
@ -77,30 +81,46 @@ public class ConstructorOperator extends Operator
return 950; return 950;
} }
public int getOperandCount() {
return methodType.getParameterTypes().length;
}
public int getOperandPriority(int i) {
return 0;
}
public Type getClassType() { public Type getClassType() {
return classType; return classType;
} }
public Type getOperandType(int i) { public void updateSubTypes() {
return methodType.getParameterTypes()[i]; Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i < paramTypes.length; i++)
subExpressions[i].setType(Type.tSubType(paramTypes[i]));
} }
public void setOperandType(Type types[]) { public void updateType() {
} }
public Expression simplifyStringBuffer() { public Expression simplifyStringBuffer() {
if (getClassType() == Type.tStringBuffer) {
if (methodType.getParameterTypes().length == 0)
return EMPTYSTRING;
if (methodType.getParameterTypes().length == 1
&& methodType.getParameterTypes()[0].equals(Type.tString))
return subExpressions[0].simplifyString();
}
return (getClassType() == Type.tStringBuffer) return (getClassType() == Type.tStringBuffer)
? EMPTYSTRING : null; ? EMPTYSTRING : null;
} }
public Expression simplify() {
InnerClassInfo outer = getOuterClassInfo();
if (outer != null && outer.outer != null
&& !Modifier.isStatic(outer.modifiers)
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) {
if (subExpressions[0] instanceof CheckNullOperator) {
CheckNullOperator cno = (CheckNullOperator) subExpressions[0];
cno.removeLocal();
subExpressions[0] = cno.subExpressions[0];
removedCheckNull = true;
}
}
return super.simplify();
}
public ClassInfo getClassInfo() { public ClassInfo getClassInfo() {
if (classType instanceof ClassInterfacesType) { if (classType instanceof ClassInterfacesType) {
return ((ClassInterfacesType) classType).getClassInfo(); return ((ClassInterfacesType) classType).getClassInfo();
@ -135,8 +155,7 @@ public class ConstructorOperator extends Operator
} }
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
int arg = 0; int arg = 0;
@ -146,8 +165,10 @@ public class ConstructorOperator extends Operator
return; return;
} }
InnerClassInfo outer = getOuterClassInfo(); InnerClassInfo outer = getOuterClassInfo();
if (outer != null && outer.name != null) { if (outer != null && outer.outer != null
Expression outExpr = operands[arg++]; && !Modifier.isStatic(outer.modifiers)
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) {
Expression outExpr = subExpressions[arg++];
if (!removedCheckNull && !(outExpr instanceof ThisOperator)) if (!removedCheckNull && !(outExpr instanceof ThisOperator))
writer.print("MISSING CHECKNULL"); writer.print("MISSING CHECKNULL");
if (outExpr instanceof ThisOperator) { if (outExpr instanceof ThisOperator) {
@ -180,10 +201,8 @@ public class ConstructorOperator extends Operator
for (int i = arg; i < methodType.getParameterTypes().length; i++) { for (int i = arg; i < methodType.getParameterTypes().length; i++) {
if (i>arg) if (i>arg)
writer.print(", "); writer.print(", ");
operands[i].dumpExpression(writer, 0); subExpressions[i].dumpExpression(writer, 0);
} }
writer.print(")"); writer.print(")");
} }
} }

@ -27,29 +27,24 @@ public class ConvertOperator extends Operator {
public ConvertOperator(Type from, Type to) { public ConvertOperator(Type from, Type to) {
super(to, 0); super(to, 0);
this.from = from; this.from = from;
initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 700; return 700;
} }
public int getOperandCount() { public void updateSubTypes() {
return 1; subExpressions[0].setType(Type.tSubType(from));
} }
public void updateType() {
public Type getOperandType(int i) {
return from;
}
public void setOperandType(Type[] inputTypes) {
from = from.intersection(inputTypes[0]);
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] ops) throws java.io.IOException { throws java.io.IOException {
writer.print("("); writer.print("(");
writer.printType(type); writer.printType(type);
writer.print(")"); writer.print(") ");
ops[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

@ -21,50 +21,117 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.GlobalOptions; import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import jode.flow.VariableSet;
public abstract class Expression { public abstract class Expression {
protected Type type; protected Type type;
Expression parent = null; Operator parent = null;
public Expression(Type type) { public Expression(Type type) {
this.type = type; this.type = type;
} }
public void setType(Type otherType) {
Type newType = otherType.intersection(type);
if (type.equals(newType))
return;
if (newType == Type.tError && otherType != Type.tError) {
GlobalOptions.err.println("setType: Type error in "+this
+": merging "+type+" and "+otherType);
if (parent != null)
GlobalOptions.err.println("\tparent is "+parent);
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0)
Thread.dumpStack();
}
type = newType;
if (type != Type.tError)
updateSubTypes();
}
public void updateParentType(Type otherType) {
Type newType = otherType.intersection(type);
if (type.equals(newType))
return;
if (newType == Type.tError) {
if (otherType == Type.tError) {
// Don't propagate type errors.
return;
}
GlobalOptions.err.println("updateParentType: Type error in "
+this+": merging "+getType()
+" and "+otherType);
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0)
Thread.dumpStack();
}
type = newType;
if (parent != null)
parent.updateType();
}
/**
* Tells an expression that an inner expression may have changed and
* that the type should be recalculated.
*
* This may call setType of the caller again.
*/
public abstract void updateType();
/**
* Tells an expression that an outer expression has changed our type
* and that the inner types should be recalculated.
*/
public abstract void updateSubTypes();
public Type getType() { public Type getType() {
return type; return type;
} }
public Expression getParent() { public Operator getParent() {
return parent; return parent;
} }
public void setType(Type otherType) {
Type newType = type.intersection(otherType);
if (newType == Type.tError
&& type != Type.tError && otherType != Type.tError)
GlobalOptions.err.println("Type error in "+this+": "
+"merging "+type+" and "+otherType);
type = newType;
}
public void updateType() { /**
if (parent != null) * Get priority of the operator.
parent.updateType(); * Currently this priorities are known:
} * <ul><li> 1000 constant
* </li><li> 950 new, .(field access), []
* </li><li> 900 new[]
* </li><li> 800 ++,-- (post)
* </li><li> 700 ++,--(pre), +,-(unary), ~, !, cast
* </li><li> 650 *,/, %
* </li><li> 610 +,-
* </li><li> 600 <<, >>, >>>
* </li><li> 550 >, <, >=, <=, instanceof
* </li><li> 500 ==, !=
* </li><li> 450 &
* </li><li> 420 ^
* </li><li> 410 |
* </li><li> 350 &&
* </li><li> 310 ||
* </li><li> 200 ?:
* </li><li> 100 =, +=, -=, etc.
* </li></ul>
*/
public abstract int getPriority();
/** /**
* Get the number of operands. * Get the number of operands.
* @return The number of stack entries this expression needs. * @return The number of stack entries this expression needs.
*/ */
public abstract int getOperandCount(); public abstract int getFreeOperandCount();
public abstract Expression addOperand(Expression op); public abstract Expression addOperand(Expression op);
public Expression negate() { public Expression negate() {
Operator negop = Operator negop =
new UnaryOperator(Type.tBoolean, Operator.LOG_NOT_OP); new UnaryOperator(Type.tBoolean, Operator.LOG_NOT_OP);
return new ComplexExpression(negop, new Expression[] { this }); negop.addOperand(this);
return negop;
} }
/** /**
@ -74,7 +141,6 @@ public abstract class Expression {
* @param expr the expression that should not change. * @param expr the expression that should not change.
*/ */
public boolean hasSideEffects(Expression expr) { public boolean hasSideEffects(Expression expr) {
// Most expression don't have side effects.
return false; return false;
} }
@ -85,21 +151,20 @@ public abstract class Expression {
* @return 1, if it can, 0, if no match was found and -1, if a * @return 1, if it can, 0, if no match was found and -1, if a
* conflict was found. You may wish to check for >0. * conflict was found. You may wish to check for >0.
*/ */
public int canCombine(Expression e) { public int canCombine(CombineableOperator combOp) {
// jode.GlobalOptions.err.println("Try to combine "+e+" into "+this); return 0;
return containsMatchingLoad(e)? 1 : 0;
} }
/** /**
* Checks if this expression contains a load, that matches the * Checks if this expression contains a load, that matches the
* given Expression (which should be a StoreInstruction/IIncOperator). * given CombineableOperator (which must be an Operator)
* @param e The store expression. * @param e The store expression.
* @return if this expression contains a matching load. * @return if this expression contains a matching load.
* @exception ClassCastException, if e.getOperator * @exception ClassCastException, if e.getOperator
* is not a CombineableOperator. * is not a CombineableOperator.
*/ */
public boolean containsMatchingLoad(Expression e) { public boolean containsMatchingLoad(CombineableOperator e) {
return ((CombineableOperator)e.getOperator()).matches(getOperator()); return false;
} }
/** /**
@ -109,7 +174,7 @@ public abstract class Expression {
* @param op The combineable operator. * @param op The combineable operator.
* @return if this expression contains a matching load. */ * @return if this expression contains a matching load. */
public boolean containsConflictingLoad(MatchableOperator op) { public boolean containsConflictingLoad(MatchableOperator op) {
return op.matches(getOperator()); return false;
} }
/** /**
@ -122,14 +187,7 @@ public abstract class Expression {
* @exception ClassCastException, if e.getOperator * @exception ClassCastException, if e.getOperator
* is not a CombineableOperator. * is not a CombineableOperator.
*/ */
public Expression combine(Expression e) { public Expression combine(CombineableOperator comb) {
CombineableOperator op = (CombineableOperator) e.getOperator();
if (op.matches(getOperator())) {
op.makeNonVoid();
/* Do not call setType, we don't want to intersect. */
e.type = e.getOperator().getType();
return e;
}
return null; return null;
} }
@ -158,8 +216,6 @@ public abstract class Expression {
return null; return null;
} }
public abstract Operator getOperator();
public void makeInitializer() { public void makeInitializer() {
} }
@ -167,24 +223,33 @@ public abstract class Expression {
return true; return true;
} }
public void fillInGenSet(VariableSet in, VariableSet gen) {
}
public abstract void dumpExpression(TabbedPrintWriter writer) public abstract void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException; throws java.io.IOException;
public void dumpExpression(TabbedPrintWriter writer, int minPriority) public void dumpExpression(TabbedPrintWriter writer, int minPriority)
throws java.io.IOException { throws java.io.IOException {
boolean needParen1 = false, needParen2 = false; boolean needParen1 = false, needParen2 = false;
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) { if (type == Type.tError) {
if (minPriority > 700) {
needParen1 = true;
writer.print("(");
}
writer.print("/*TYPE ERROR*/ ");
minPriority = 700;
}
else if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_TYPES) != 0) {
if (minPriority > 700) { if (minPriority > 700) {
needParen1 = true; needParen1 = true;
writer.print("("); writer.print("(");
} }
writer.print("("+getType()+") "); writer.print("(TYPE "+type+") ");
minPriority = 700; minPriority = 700;
} else if (getType() == Type.tError) {
writer.print("(/*type error */");
needParen1 = true;
} }
if (getOperator().getPriority() < minPriority) { if (getPriority() < minPriority) {
needParen2 = true; needParen2 = true;
writer.print("("); writer.print("(");
} }
@ -208,7 +273,6 @@ public abstract class Expression {
} }
} }
public boolean isVoid() { public boolean isVoid() {
return getType() == Type.tVoid; return getType() == Type.tVoid;
} }

@ -35,7 +35,6 @@ public class GetFieldOperator extends Operator {
CodeAnalyzer codeAnalyzer; CodeAnalyzer codeAnalyzer;
Reference ref; Reference ref;
Type classType; Type classType;
boolean needCast = false;
public GetFieldOperator(CodeAnalyzer codeAnalyzer, boolean staticFlag, public GetFieldOperator(CodeAnalyzer codeAnalyzer, boolean staticFlag,
Reference ref) { Reference ref) {
@ -46,27 +45,18 @@ public class GetFieldOperator extends Operator {
this.ref = ref; this.ref = ref;
if (staticFlag) if (staticFlag)
codeAnalyzer.useType(classType); codeAnalyzer.useType(classType);
initOperands(staticFlag ? 0 : 1);
} }
public int getPriority() { public int getPriority() {
return 950; return 950;
} }
public int getOperandCount() { public void updateSubTypes() {
return staticFlag?0:1;
}
public int getOperandPriority(int i) {
return 900;
}
public Type getOperandType(int i) {
return classType;
}
public void setOperandType(Type types[]) {
if (!staticFlag) if (!staticFlag)
needCast = types[0].getHint().equals(Type.tNull); subExpressions[0].setType(Type.tSubType(classType));
}
public void updateType() {
} }
/** /**
@ -98,7 +88,7 @@ public class GetFieldOperator extends Operator {
return null; return null;
} }
public FieldAnalyzer getFieldAnalyzer() { public FieldAnalyzer getField() {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = getClassInfo();
if (clazz != null) { if (clazz != null) {
ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer(); ClassAnalyzer ana = codeAnalyzer.getClassAnalyzer();
@ -119,9 +109,10 @@ public class GetFieldOperator extends Operator {
return null; return null;
} }
public void dumpExpression(TabbedPrintWriter writer, Expression[] operands) public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
boolean opIsThis = !staticFlag && operands[0] instanceof ThisOperator; boolean opIsThis = !staticFlag
&& subExpressions[0] instanceof ThisOperator;
String fieldName = ref.getName(); String fieldName = ref.getName();
if (staticFlag) { if (staticFlag) {
if (!classType.equals(Type.tClass(codeAnalyzer.getClazz())) if (!classType.equals(Type.tClass(codeAnalyzer.getClazz()))
@ -130,48 +121,56 @@ public class GetFieldOperator extends Operator {
writer.print("."); writer.print(".");
} }
writer.print(fieldName); writer.print(fieldName);
} else if (operands[0].getType() instanceof NullType) { } else if (subExpressions[0].getType() instanceof NullType) {
writer.print("(("); writer.print("((");
writer.printType(classType); writer.printType(classType);
writer.print(")"); writer.print(")");
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
writer.print(")."); writer.print(").");
writer.print(fieldName); writer.print(fieldName);
} else { } else {
if (opIsThis) { if (opIsThis) {
ThisOperator thisOp = (ThisOperator) operands[0]; ThisOperator thisOp = (ThisOperator) subExpressions[0];
Scope scope = writer.getScope(thisOp.getClassInfo(), Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (scope == null)
writer.println("UNKNOWN ");
if (scope == null || writer.conflicts(fieldName, scope, if (scope == null || writer.conflicts(fieldName, scope,
Scope.FIELDNAME)) { Scope.FIELDNAME)) {
thisOp.dumpExpression(writer, 950); thisOp.dumpExpression(writer, 950);
writer.print("."); writer.print(".");
} else if (writer.conflicts(fieldName, scope, } else if (writer.conflicts(fieldName, scope,
Scope.AMBIGUOUSNAME)) { Scope.AMBIGUOUSNAME)
|| (/* This is a inherited field conflicting
* with a field name in some outer class.
*/
getField() == null
&& writer.conflicts(fieldName, null,
Scope.FIELDNAME))) {
writer.print("this."); writer.print("this.");
} }
} else { } else {
operands[0].dumpExpression(writer, 950); subExpressions[0].dumpExpression(writer, 950);
writer.print("."); writer.print(".");
} }
writer.print(fieldName); writer.print(fieldName);
} }
} }
public Expression simplifyThis(Expression[] subs) { public Expression simplify() {
if (!staticFlag && subs[0] instanceof ThisOperator) { if (!staticFlag) {
Expression constant = getFieldAnalyzer().getConstant(); subExpressions[0] = subExpressions[0].simplify();
if (constant instanceof ThisOperator) subExpressions[0].parent = this;
return constant; if (subExpressions[0] instanceof ThisOperator
&& getField() != null) {
Expression constant = getField().getConstant();
if (constant instanceof ThisOperator)
return constant;
}
} }
return null; return this;
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return o instanceof GetFieldOperator return o instanceof GetFieldOperator
&& ((GetFieldOperator)o).ref.equals(ref); && ((GetFieldOperator)o).ref.equals(ref);
} }

@ -22,53 +22,38 @@ import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class IIncOperator extends NoArgOperator public class IIncOperator extends Operator
implements LocalVarOperator, CombineableOperator { implements CombineableOperator {
String value; int value;
LocalInfo local;
public IIncOperator(LocalInfo local, String value, int operator) { public IIncOperator(LocalStoreOperator localStore, int value,
int operator) {
super(Type.tVoid, operator); super(Type.tVoid, operator);
this.local = local;
this.value = value; this.value = value;
local.setType(Type.tUInt); initOperands(1);
local.setOperator(this); setSubExpressions(0, localStore);
} }
public String getValue() { public LValueExpression getLValue() {
return value; return (LValueExpression) subExpressions[0];
}
public boolean isRead() {
return true;
}
public boolean isWrite() {
return true;
}
public void updateType() {
if (parent != null)
parent.updateType();
} }
public LocalInfo getLocalInfo() { public String getValue() {
return local; return Integer.toString(value);
} }
public int getPriority() { public int getPriority() {
return 100; return 100;
} }
public void updateSubTypes() {
subExpressions[0].setType(type != Type.tVoid ? type : Type.tUInt);
}
/**
* Checks if the value of the given expression can change, due to public void updateType() {
* side effects in this expression. If this returns false, the if (type != Type.tVoid)
* expression can safely be moved behind the current expresion. updateParentType(subExpressions[0].getType());
* @param expr the expression that should not change.
*/
public boolean hasSideEffects(Expression expr) {
return expr.containsConflictingLoad(this);
} }
/** /**
@ -77,29 +62,27 @@ public class IIncOperator extends NoArgOperator
public void makeNonVoid() { public void makeNonVoid() {
if (type != Type.tVoid) if (type != Type.tVoid)
throw new jode.AssertError("already non void"); throw new jode.AssertError("already non void");
type = local.getType(); type = subExpressions[0].getType();
} }
public boolean matches(Operator loadop) { public boolean lvalueMatches(Operator loadop) {
return loadop instanceof LocalLoadOperator && return getLValue().matches(loadop);
((LocalLoadOperator)loadop).getLocalInfo().getLocalInfo()
== local.getLocalInfo();
} }
public Expression simplify() { public Expression simplify() {
if (value.equals("1")) { if (value == 1) {
int op = (getOperatorIndex() == OPASSIGN_OP+ADD_OP) int op = (getOperatorIndex() == OPASSIGN_OP+ADD_OP)
? INC_OP : DEC_OP; ? INC_OP : DEC_OP;
return new PrePostFixOperator
return new LocalPrePostFixOperator (getType(), op, getLValue(), isVoid()).simplify();
(getType(), op, this, isVoid()).simplify();
} }
return super.simplify(); return super.simplify();
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
writer.print(local.getName() + getOperatorString() + value); subExpressions[0].dumpExpression(writer, 950);
writer.print(getOperatorString() + value);
} }
} }

@ -19,52 +19,91 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.FieldAnalyzer;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class IfThenElseOperator extends SimpleOperator { public class IfThenElseOperator extends Operator {
public IfThenElseOperator(Type type) { public IfThenElseOperator(Type type) {
super(type, 0, 3); super(type, 0);
operandTypes[0] = Type.tBoolean; initOperands(3);
} }
public int getOperandCount() {
return 3;
}
public int getPriority() { public int getPriority() {
return 200; return 200;
} }
public void setOperandType(Type[] inputTypes) { public void updateSubTypes() {
super.setOperandType(inputTypes); subExpressions[0].setType(Type.tBoolean);
Type operandType = subExpressions[1].setType(Type.tSubType(type));
type.intersection(operandTypes[1]).intersection(operandTypes[2]); subExpressions[2].setType(Type.tSubType(type));
type = operandTypes[1] = operandTypes[2] = operandType; }
public void updateType() {
Type subType = Type.tSuperType(subExpressions[1].getType())
.intersection(Type.tSuperType(subExpressions[2].getType()));
updateParentType(subType);
} }
/** public Expression simplify() {
* Sets the return type of this operator. if (getType().isOfType(Type.tBoolean)) {
* @return true if the operand types changed if (subExpressions[1] instanceof ConstOperator
*/ && subExpressions[2] instanceof ConstOperator) {
public void setType(Type newType) { ConstOperator c1 = (ConstOperator) subExpressions[1];
Type operandType = ConstOperator c2 = (ConstOperator) subExpressions[2];
type.intersection(operandTypes[1]).intersection(newType); if (c1.getValue().equals("1") &&
if (!type.equals(operandType)) { c2.getValue().equals("0"))
type = operandTypes[1] = operandTypes[2] = operandType; return subExpressions[0].simplify();
if (c2.getValue().equals("1") &&
c1.getValue().equals("0"))
return subExpressions[0].negate().simplify();
}
} }
if (subExpressions[0] instanceof CompareUnaryOperator
&& (subExpressions[1] instanceof GetFieldOperator)
&& (subExpressions[2] instanceof StoreInstruction)) {
// Check for
// class$classname != null ? class$classname :
// (class$classname = class$("classname"))
// and replace with
// classname.class
CompareUnaryOperator cmp
= (CompareUnaryOperator) subExpressions[0];
GetFieldOperator get = (GetFieldOperator) subExpressions[1];
StoreInstruction put = (StoreInstruction) subExpressions[2];
FieldAnalyzer field;
if (cmp.getOperatorIndex() == Operator.NOTEQUALS_OP
&& put.getLValue() instanceof PutFieldOperator
&& ((field = ((PutFieldOperator)put.getLValue()).getField())
!= null) && field.isSynthetic()
&& put.lvalueMatches(get)
&& cmp.subExpressions[0] instanceof GetFieldOperator
&& put.lvalueMatches((GetFieldOperator)cmp.subExpressions[0])
&& put.subExpressions[1] instanceof InvokeOperator) {
InvokeOperator invoke = (InvokeOperator) put.subExpressions[1];
if (invoke.isGetClass()
&& invoke.subExpressions[0] instanceof ConstOperator
&& (invoke.subExpressions[0].getType()
.equals(Type.tString))) {
String clazz =
((ConstOperator)invoke.subExpressions[0]).getValue();
if (field.setClassConstant(clazz))
return new ClassFieldOperator(Type.tClass(clazz));
}
}
}
return super.simplify();
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return (o instanceof IfThenElseOperator); return (o instanceof IfThenElseOperator);
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 201); subExpressions[0].dumpExpression(writer, 201);
writer.print(" ? "); writer.print(" ? ");
operands[1].dumpExpression(writer, 0); subExpressions[1].dumpExpression(writer, 0);
writer.print(" : "); writer.print(" : ");
operands[2].dumpExpression(writer, 200); subExpressions[2].dumpExpression(writer, 200);
} }
} }

@ -21,50 +21,42 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class InstanceOfOperator extends SimpleOperator { public class InstanceOfOperator extends Operator {
Type instanceType; Type instanceType;
/**
* There are special cases where a instanceof isn't allowed. We must cast
* to the common super type before. This cases always give a runtime
* error, but we want to decompile even bad programs.
*/
Type superType = null;
public InstanceOfOperator(Type type) { public InstanceOfOperator(Type type) {
super(Type.tBoolean, 0, 1); super(Type.tBoolean, 0);
this.instanceType = type; this.instanceType = type;
this.operandTypes[0] = Type.tUnknown; initOperands(1);
}
public int getOperandCount() {
return 1;
} }
public int getPriority() { public int getPriority() {
return 550; return 550;
} }
public void setOperandType(Type[] type) { public void updateSubTypes() {
super.setOperandType(type); subExpressions[0].setType(Type.tUObject);
superType = instanceType.getCastHelper(type[0]); }
public void updateType() {
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
/* There are special cases where a cast isn't allowed. We must cast /* There are special cases where a cast isn't allowed. We must cast
* to the common super type before. This cases always give a runtime * to the common super type before. This cases always give a runtime
* error, but we want to decompile even bad programs. * error, but we want to decompile even bad programs.
*/ */
Type superType = instanceType.getCastHelper(operands[0].getType()); Type superType
= instanceType.getCastHelper(subExpressions[0].getType());
if (superType != null) { if (superType != null) {
writer.print("("); writer.print("(");
writer.printType(superType); writer.printType(superType);
writer.print(") "); writer.print(") ");
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} else } else
operands[0].dumpExpression(writer, 550); subExpressions[0].dumpExpression(writer, 550);
writer.print(" instanceof "); writer.print(" instanceof ");
writer.printType(instanceType); writer.printType(instanceType);
} }

@ -18,6 +18,8 @@
*/ */
package jode.expr; package jode.expr;
import java.lang.reflect.Modifier;
import jode.Decompiler; import jode.Decompiler;
import jode.decompiler.CodeAnalyzer; import jode.decompiler.CodeAnalyzer;
import jode.decompiler.MethodAnalyzer; import jode.decompiler.MethodAnalyzer;
@ -43,7 +45,7 @@ public final class InvokeOperator extends Operator
boolean staticFlag, boolean specialFlag, boolean staticFlag, boolean specialFlag,
Reference reference) { Reference reference) {
super(Type.tUnknown, 0); super(Type.tUnknown, 0);
this.methodType = (MethodType) Type.tType(reference.getType()); this.methodType = Type.tMethod(reference.getType());
this.methodName = reference.getName(); this.methodName = reference.getName();
this.classType = Type.tType(reference.getClazz()); this.classType = Type.tType(reference.getClazz());
this.type = methodType.getReturnType(); this.type = methodType.getReturnType();
@ -52,25 +54,8 @@ public final class InvokeOperator extends Operator
this.specialFlag = specialFlag; this.specialFlag = specialFlag;
if (staticFlag) if (staticFlag)
codeAnalyzer.useType(classType); codeAnalyzer.useType(classType);
} initOperands((staticFlag ? 0 : 1)
+ methodType.getParameterTypes().length);
/**
* Checks if the value of the given expression can change, due to
* side effects in this expression. If this returns false, the
* expression can safely be moved behind the current expresion.
* @param expr the expression that should not change.
*/
public boolean hasSideEffects(Expression expr) {
return expr.containsConflictingLoad(this);
}
/**
* Checks if the value of the operator can be changed by this expression.
*/
public boolean matches(Operator loadop) {
return (loadop instanceof InvokeOperator
|| loadop instanceof ConstructorOperator
|| loadop instanceof GetFieldOperator);
} }
public final boolean isStatic() { public final boolean isStatic() {
@ -93,21 +78,16 @@ public final class InvokeOperator extends Operator
return 950; return 950;
} }
public int getOperandCount() { public void updateSubTypes() {
return (isStatic()?0:1) int offset = 0;
+ methodType.getParameterTypes().length;
}
public Type getOperandType(int i) {
if (!isStatic()) { if (!isStatic()) {
if (i == 0) subExpressions[offset++].setType(Type.tSubType(getClassType()));
return getClassType();
i--;
} }
return methodType.getParameterTypes()[i]; Type[] paramTypes = methodType.getParameterTypes();
for (int i=0; i < paramTypes.length; i++)
subExpressions[offset++].setType(Type.tSubType(paramTypes[i]));
} }
public void updateType() {
public void setOperandType(Type types[]) {
} }
public boolean isConstructor() { public boolean isConstructor() {
@ -129,13 +109,12 @@ public final class InvokeOperator extends Operator
return getClassInfo() == codeAnalyzer.getClazz(); return getClassInfo() == codeAnalyzer.getClazz();
} }
public ClassInfo getOuterClass() { public InnerClassInfo getOuterClassInfo() {
ClassInfo clazz = getClassInfo(); ClassInfo clazz = getClassInfo();
if (clazz != null) { if (clazz != null) {
InnerClassInfo[] outers = clazz.getOuterClasses(); InnerClassInfo[] outers = clazz.getOuterClasses();
if (outers != null && outers[0].outer != null if (outers != null)
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) return outers[0];
return ClassInfo.forName(outers[0].outer);
} }
return null; return null;
} }
@ -202,90 +181,13 @@ public final class InvokeOperator extends Operator
return false; return false;
} }
public void dumpExpression(TabbedPrintWriter writer, /**
Expression[] operands) * Checks if the value of the operator can be changed by this expression.
throws java.io.IOException { */
boolean opIsThis = !staticFlag && operands[0] instanceof ThisOperator; public boolean matches(Operator loadop) {
int arg = 1; return (loadop instanceof InvokeOperator
|| loadop instanceof ConstructorOperator
if (isConstructor()) { || loadop instanceof GetFieldOperator);
ClassInfo outer = getOuterClass();
if (outer != null) {
operands[arg++].dumpExpression(writer, 0);
writer.print(".");
}
}
if (specialFlag) {
if (opIsThis
&& (((ThisOperator)operands[0]).getClassInfo()
== codeAnalyzer.getClazz())) {
if (isThis()) {
/* XXX check if this is a private or final method. */
} else {
/* XXX check that this is the first defined
* super method. */
writer.print("super");
opIsThis = false;
}
} else {
/* XXX check if this is a private or final method. */
int minPriority = 950; /* field access */
if (!isThis()) {
writer.print("(NON VIRTUAL ");
writer.printType(classType);
writer.print(")");
minPriority = 700;
}
operands[0].dumpExpression(writer, minPriority);
}
} else if (staticFlag) {
arg = 0;
Scope scope = writer.getScope(getClassInfo(),
Scope.CLASSSCOPE);
if (scope != null
&& !writer.conflicts(methodName, scope, Scope.METHODNAME))
opIsThis = true;
else
writer.printType(classType);
} else {
if (opIsThis) {
ThisOperator thisOp = (ThisOperator) operands[0];
Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE);
if (writer.conflicts(methodName, scope, Scope.METHODNAME)) {
thisOp.dumpExpression(writer, 950);
writer.print(".");
}
} else {
if (operands[0].getType() instanceof NullType) {
writer.print("((");
writer.printType(classType);
writer.print(") ");
operands[0].dumpExpression(writer, 700);
writer.print(")");
} else
operands[0].dumpExpression(writer, 950);
}
}
if (isConstructor()) {
if (opIsThis)
writer.print("this");
} else {
if (!opIsThis)
writer.print(".");
writer.print(methodName);
}
writer.print("(");
boolean first = true;
while (arg < operands.length) {
if (!first)
writer.print(", ");
else
first = false;
operands[arg++].dumpExpression(writer, 0);
}
writer.print(")");
} }
/** /**
@ -329,11 +231,6 @@ public final class InvokeOperator extends Operator
} }
public ConstOperator deobfuscateString(ConstOperator op) { public ConstOperator deobfuscateString(ConstOperator op) {
if (!isThis() || !isStatic()
|| methodType.getParameterTypes().length != 1
|| !methodType.getParameterTypes()[0].equals(Type.tString)
|| !methodType.getReturnType().equals(Type.tString))
return null;
ClassAnalyzer clazz = codeAnalyzer.getClassAnalyzer(); ClassAnalyzer clazz = codeAnalyzer.getClassAnalyzer();
CodeAnalyzer ca = clazz.getMethod(methodName, methodType).getCode(); CodeAnalyzer ca = clazz.getMethod(methodName, methodType).getCode();
if (ca == null) if (ca == null)
@ -361,8 +258,89 @@ public final class InvokeOperator extends Operator
return new ConstOperator(result); return new ConstOperator(result);
} }
public Expression simplifyAccess(Expression[] subs) { public Expression simplifyStringBuffer() {
if (isOuter()) { if (getClassType().equals(Type.tStringBuffer)
&& !isStatic()
&& getMethodName().equals("append")
&& getMethodType().getParameterTypes().length == 1) {
Expression e = subExpressions[0].simplifyStringBuffer();
if (e == null)
return null;
subExpressions[1] = subExpressions[1].simplifyString();
if (e == EMPTYSTRING
&& subExpressions[1].getType().isOfType(Type.tString))
return subExpressions[1];
if (e instanceof StringAddOperator
&& ((Operator)e).getSubExpressions()[0] == EMPTYSTRING)
e = ((Operator)e).getSubExpressions()[1];
Operator result = new StringAddOperator();
result.addOperand(subExpressions[1]);
result.addOperand(e);
return result;
}
return null;
}
public Expression simplifyString() {
if (getMethodName().equals("toString")
&& !isStatic()
&& getClassType().equals(Type.tStringBuffer)
&& subExpressions.length == 1) {
Expression simple = subExpressions[0].simplifyStringBuffer();
if (simple != null)
return simple;
}
else if (getMethodName().equals("valueOf")
&& isStatic()
&& getClassType().equals(Type.tString)
&& subExpressions.length == 1) {
if (subExpressions[0].getType().isOfType(Type.tString))
return subExpressions[0];
Operator op = new StringAddOperator();
op.addOperand(subExpressions[0]);
op.addOperand(EMPTYSTRING);
}
/* The pizza way (pizza is the compiler of kaffe) */
else if (getMethodName().equals("concat")
&& !isStatic()
&& getClassType().equals(Type.tString)) {
Expression result = new StringAddOperator();
Expression right = subExpressions[1].simplify();
if (right instanceof StringAddOperator) {
Operator op = (Operator) right;
if (op.subExpressions != null
&& op.subExpressions[0] == EMPTYSTRING)
right = op.subExpressions[1];
}
result.addOperand(right);
result.addOperand(subExpressions[0].simplify());
}
else if ((Decompiler.options & Decompiler.OPTION_DECRYPT) != 0
&& isThis() && isStatic()
&& methodType.getParameterTypes().length == 1
&& methodType.getParameterTypes()[0].equals(Type.tString)
&& methodType.getReturnType().equals(Type.tString)) {
Expression expr = subExpressions[0].simplifyString();
if (expr instanceof ConstOperator) {
expr = deobfuscateString((ConstOperator)expr);
if (expr != null)
return expr;
}
}
return this;
}
public Expression simplifyAccess() {
if (isOuter() && getMethodAnalyzer() != null) {
SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic(); SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
if (synth != null) { if (synth != null) {
Operator op = null; Operator op = null;
@ -376,12 +354,14 @@ public final class InvokeOperator extends Operator
synth.getReference()); synth.getReference());
break; break;
case SyntheticAnalyzer.ACCESSPUTFIELD: case SyntheticAnalyzer.ACCESSPUTFIELD:
op = new PutFieldOperator(codeAnalyzer, false, op = new StoreInstruction
synth.getReference()); (new PutFieldOperator(codeAnalyzer, false,
synth.getReference()));
break; break;
case SyntheticAnalyzer.ACCESSPUTSTATIC: case SyntheticAnalyzer.ACCESSPUTSTATIC:
op = new PutFieldOperator(codeAnalyzer, true, op = new StoreInstruction
synth.getReference()); (new PutFieldOperator(codeAnalyzer, true,
synth.getReference()));
break; break;
case SyntheticAnalyzer.ACCESSMETHOD: case SyntheticAnalyzer.ACCESSMETHOD:
op = new InvokeOperator(codeAnalyzer, false, op = new InvokeOperator(codeAnalyzer, false,
@ -394,9 +374,11 @@ public final class InvokeOperator extends Operator
} }
if (op != null) { if (op != null) {
if (subs == null) if (subExpressions != null) {
return op; for (int i=subExpressions.length; i-- > 0; )
return new ComplexExpression(op, subs); op.addOperand(subExpressions[i]);
}
return op;
} }
} }
} }
@ -404,9 +386,12 @@ public final class InvokeOperator extends Operator
} }
public Expression simplify() { public Expression simplify() {
Expression expr = simplifyAccess(null); Expression expr = simplifyAccess();
if (expr != null) if (expr != null)
return expr.simplify(); return expr.simplify();
expr = simplifyString();
if (expr != this)
return expr.simplify();
return super.simplify(); return super.simplify();
} }
@ -414,4 +399,102 @@ public final class InvokeOperator extends Operator
/* Invokes never equals: they may return different values even if /* Invokes never equals: they may return different values even if
* they have the same parameters. * they have the same parameters.
*/ */
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
boolean opIsThis = !staticFlag
&& subExpressions[0] instanceof ThisOperator;
int arg = 1;
if (isConstructor()) {
InnerClassInfo outer = getOuterClassInfo();
if (outer != null && outer.outer != null
&& !Modifier.isStatic(outer.modifiers)
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) {
Expression outerInstance = subExpressions[arg++];
if (!(outerInstance instanceof ThisOperator)) {
outerInstance.dumpExpression(writer, 0);
writer.print(".");
}
}
}
if (specialFlag) {
if (opIsThis
&& (((ThisOperator)subExpressions[0]).getClassInfo()
== codeAnalyzer.getClazz())) {
if (isThis()) {
/* XXX check if this is a private or final method. */
} else {
/* XXX check that this is the first defined
* super method. */
writer.print("super");
opIsThis = false;
}
} else {
/* XXX check if this is a private or final method. */
int minPriority = 950; /* field access */
if (!isThis()) {
writer.print("(NON VIRTUAL ");
writer.printType(classType);
writer.print(")");
minPriority = 700;
}
subExpressions[0].dumpExpression(writer, minPriority);
}
} else if (staticFlag) {
arg = 0;
Scope scope = writer.getScope(getClassInfo(),
Scope.CLASSSCOPE);
if (scope != null
&& !writer.conflicts(methodName, scope, Scope.METHODNAME))
opIsThis = true;
else
writer.printType(classType);
} else {
if (opIsThis) {
ThisOperator thisOp = (ThisOperator) subExpressions[0];
Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE);
if (writer.conflicts(methodName, scope, Scope.METHODNAME)) {
thisOp.dumpExpression(writer, 950);
writer.print(".");
} else if (/* This is a inherited field conflicting
* with a field name in some outer class.
*/
getMethodAnalyzer() == null
&& writer.conflicts(methodName, null,
Scope.METHODNAME)) {
writer.print("this.");
}
} else {
if (subExpressions[0].getType() instanceof NullType) {
writer.print("((");
writer.printType(classType);
writer.print(") ");
subExpressions[0].dumpExpression(writer, 700);
writer.print(")");
} else
subExpressions[0].dumpExpression(writer, 950);
}
}
if (isConstructor()) {
if (opIsThis)
writer.print("this");
} else {
if (!opIsThis)
writer.print(".");
writer.print(methodName);
}
writer.print("(");
boolean first = true;
while (arg < subExpressions.length) {
if (!first)
writer.print(", ");
else
first = false;
subExpressions[arg++].dumpExpression(writer, 0);
}
writer.print(")");
}
} }

@ -0,0 +1,31 @@
/* LValueExpression Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.expr;
import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter;
public abstract class LValueExpression extends Operator
implements MatchableOperator {
public LValueExpression(Type lvalueType) {
super(lvalueType, 0);
}
}

@ -25,7 +25,7 @@ import jode.decompiler.ClassAnalyzer;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class LocalLoadOperator extends NoArgOperator public class LocalLoadOperator extends Operator
implements LocalVarOperator { implements LocalVarOperator {
CodeAnalyzer codeAnalyzer; CodeAnalyzer codeAnalyzer;
LocalInfo local; LocalInfo local;
@ -35,8 +35,8 @@ public class LocalLoadOperator extends NoArgOperator
super(type); super(type);
this.codeAnalyzer = codeAnalyzer; this.codeAnalyzer = codeAnalyzer;
this.local = local; this.local = local;
local.setType(type);
local.setOperator(this); local.setOperator(this);
initOperands(0);
} }
public boolean isRead() { public boolean isRead() {
@ -51,43 +51,30 @@ public class LocalLoadOperator extends NoArgOperator
return false; return false;
} }
public int getPriority() {
return 1000;
}
public LocalInfo getLocalInfo() { public LocalInfo getLocalInfo() {
return local.getLocalInfo(); return local.getLocalInfo();
} }
public void updateSubTypes() {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("setType of "+local.getName()+": "
+local.getType());
local.setType(type);
}
public void updateType() { public void updateType() {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0) if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("local "+local.getName()+" changed: " GlobalOptions.err.println("local "+local.getName()+" changed: "
+type+" to "+local.getType() +type+" to "+local.getType()
+" in "+parent); +" in "+parent);
super.setType(local.getType()); updateParentType(local.getType());
if (parent != null)
parent.updateType();
} }
public int getPriority() { public boolean opEquals(Operator o) {
return 1000;
}
public Type getType() {
// GlobalOptions.err.println("LocalLoad.getType of "+local.getName()+": "+local.getType());
return local.getType();
}
public void setType(Type type) {
// GlobalOptions.err.println("LocalLoad.setType of "+local.getName()+": "+local.getType());
super.setType(local.setType(type));
}
// public int getSlot() {
// return slot;
// }
public String toString() {
return local.getName().toString();
}
public boolean equals(Object o) {
return (o instanceof LocalLoadOperator && return (o instanceof LocalLoadOperator &&
((LocalLoadOperator) o).local.getSlot() == local.getSlot()); ((LocalLoadOperator) o).local.getSlot() == local.getSlot());
} }
@ -102,10 +89,8 @@ public class LocalLoadOperator extends NoArgOperator
return super.simplify(); return super.simplify();
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
writer.print(toString()); writer.print(local.getName());
} }
} }

@ -22,72 +22,48 @@ import jode.type.Type;
import jode.decompiler.LocalInfo; import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class LocalStoreOperator extends StoreInstruction public class LocalStoreOperator extends LValueExpression
implements LocalVarOperator { implements LocalVarOperator {
LocalInfo local; LocalInfo local;
public LocalStoreOperator(Type lvalueType, LocalInfo local, int operator) { public LocalStoreOperator(Type lvalueType, LocalInfo local) {
super(lvalueType, operator); super(lvalueType);
this.local = local; this.local = local;
local.setType(lvalueType);
local.setOperator(this); local.setOperator(this);
initOperands(0);
} }
public boolean isRead() { public boolean isRead() {
return operator != ASSIGN_OP; return parent != null && parent.getOperatorIndex() != ASSIGN_OP;
} }
public boolean isWrite() { public boolean isWrite() {
return true; return true;
} }
public void updateSubTypes() {
local.setType(type);
}
public void updateType() { public void updateType() {
if (parent != null) updateParentType(local.getType());
parent.updateType();
} }
public LocalInfo getLocalInfo() { public LocalInfo getLocalInfo() {
return local.getLocalInfo(); return local.getLocalInfo();
} }
public Type getLValueType() {
return local.getType();
}
public void setLValueType(Type type) {
local.setType(type);
}
public boolean matches(Operator loadop) { public boolean matches(Operator loadop) {
return loadop instanceof LocalLoadOperator && return loadop instanceof LocalLoadOperator &&
((LocalLoadOperator)loadop).getLocalInfo().getSlot() ((LocalLoadOperator)loadop).getLocalInfo().getSlot()
== local.getSlot(); == local.getSlot();
} }
public int getLValuePriority() { public int getPriority() {
return 1000; return 1000;
} }
public int getLValueOperandCount() { public void dumpExpression(TabbedPrintWriter writer) {
return 0;
}
public int getLValueOperandPriority(int i) {
/* shouldn't be called */
throw new RuntimeException("LocalStoreOperator has no operands");
}
public Type getLValueOperandType(int i) {
/* shouldn't be called */
throw new RuntimeException("LocalStoreOperator has no operands");
}
public void setLValueOperandType(Type []t) {
/* shouldn't be called */
throw new RuntimeException("LocalStoreOperator has no operands");
}
public void dumpLValue(TabbedPrintWriter writer, Expression[] operands) {
writer.print(local.getName()); writer.print(local.getName());
} }
} }

@ -31,5 +31,3 @@ public interface LocalVarOperator {
*/ */
public void updateType(); public void updateType();
} }

@ -21,20 +21,26 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class MonitorEnterOperator extends SimpleOperator { public class MonitorEnterOperator extends Operator {
public MonitorEnterOperator() { public MonitorEnterOperator() {
super(Type.tVoid, 0, 1); super(Type.tVoid, 0);
operandTypes[0] = Type.tObject; initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 700; return 700;
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateSubTypes() {
Expression[] operands) subExpressions[0].setType(Type.tUObject);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
writer.print("MONITORENTER "); writer.print("MONITORENTER ");
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

@ -21,20 +21,26 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class MonitorExitOperator extends SimpleOperator { public class MonitorExitOperator extends Operator {
public MonitorExitOperator() { public MonitorExitOperator() {
super(Type.tVoid, 0, 1); super(Type.tVoid, 0);
operandTypes[0] = Type.tObject; initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 700; return 700;
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateSubTypes() {
Expression[] operands) subExpressions[0].setType(Type.tUObject);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
writer.print("MONITOREXIT "); writer.print("MONITOREXIT ");
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

@ -22,22 +22,31 @@ import jode.type.Type;
import jode.type.ArrayType; import jode.type.ArrayType;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class NewArrayOperator extends SimpleOperator { public class NewArrayOperator extends Operator {
String baseTypeString; String baseTypeString;
public NewArrayOperator(Type arrayType, int dimensions) { public NewArrayOperator(Type arrayType, int dimensions) {
super(arrayType, 0, dimensions); super(arrayType, 0);
for (int i=0; i< dimensions; i++) { initOperands(dimensions);
operandTypes[i] = Type.tUInt; }
}
public int getDimensions() {
return subExpressions.length;
} }
public int getPriority() { public int getPriority() {
return 900; return 900;
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateSubTypes() {
Expression[] operands) for (int i=0; i< subExpressions.length; i++)
subExpressions[i].setType(Type.tUInt);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
Type flat = type; Type flat = type;
int depth = 0; int depth = 0;
@ -49,8 +58,8 @@ public class NewArrayOperator extends SimpleOperator {
writer.printType(flat); writer.printType(flat);
for (int i=0; i< depth; i++) { for (int i=0; i< depth; i++) {
writer.print("["); writer.print("[");
if (i < getOperandCount()) if (i < subExpressions.length)
operands[i].dumpExpression(writer, 0); subExpressions[i].dumpExpression(writer, 0);
writer.print("]"); writer.print("]");
} }
} }

@ -30,8 +30,8 @@ public class NewOperator extends NoArgOperator {
return 950; return 950;
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] expr) throws java.io.IOException{ throws java.io.IOException {
writer.print("new "); writer.print("new ");
writer.printType(type); writer.printType(type);
} }

@ -26,25 +26,15 @@ public abstract class NoArgOperator extends Operator {
public NoArgOperator(Type type, int operator) { public NoArgOperator(Type type, int operator) {
super(type, operator); super(type, operator);
initOperands(0);
} }
public NoArgOperator(Type type) { public NoArgOperator(Type type) {
this(type, 0); this(type, 0);
} }
public int getOperandCount() { public void updateType() {
return 0;
} }
public void updateSubTypes() {
public int getOperandPriority(int i) {
throw new AssertError("This operator has no operands");
}
public Type getOperandType(int i) {
throw new AssertError("This operator has no operands");
}
public void setOperandType(Type[] types) {
throw new AssertError("This operator has no operands");
} }
} }

@ -22,33 +22,36 @@ import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
/** /**
* A NopOperator takes one or zero arguments and returns it again. It * A NopOperator takes one arguments and returns it again. It is used
* is mainly used as placeholder when the real operator is not yet * as placeholder when the real operator is not yet known (e.g. in
* known (e.g. in SwitchBlock). But there also exists a nop opcode in * SwitchBlock, but also in every other Operator).
* the java virtual machine (The compiler can't create such a opcode, *
* though). * A Nop operator doesn't have subExpressions; getSubExpressions() simply
* returns zero.
* *
* @author Jochen Hoenicke */ * @author Jochen Hoenicke */
public class NopOperator extends SimpleOperator { public class NopOperator extends Expression {
public NopOperator(Type type) { public NopOperator(Type type) {
super(type, 0, 1); super(type);
} }
public NopOperator() { public int getFreeOperandCount() {
super(Type.tVoid, 0, 0); return 1;
} }
public int getPriority() { public int getPriority() {
return 1000; return 1000;
} }
public Expression addOperand(Expression op) { public void updateSubTypes() {
op.setType(type.getSubType()); }
return op; public void updateType() {
} }
public int getOperandPriority(int i) { public Expression addOperand(Expression op) {
return 0; op.setType(type);
op.parent = parent;
return op;
} }
public boolean isConstant() { public boolean isConstant() {
@ -59,15 +62,12 @@ public class NopOperator extends SimpleOperator {
return (o instanceof NopOperator); return (o instanceof NopOperator);
} }
public void dumpExpression(TabbedPrintWriter writer, public Expression simplify() {
Expression[] operands) return this;
throws java.io.IOException {
if (type == Type.tVoid)
writer.print("/* NOP */");
operands[0].dumpExpression(writer);
} }
public void dumpExpression(TabbedPrintWriter writer) { public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
writer.print("POP"); writer.print("POP");
} }
} }

@ -19,7 +19,9 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import jode.flow.VariableSet;
public abstract class Operator extends Expression { public abstract class Operator extends Expression {
public final static int ADD_OP = 1; public final static int ADD_OP = 1;
@ -33,6 +35,10 @@ public abstract class Operator extends Expression {
public final static int COMPARE_OP = 26; /* must be even! */ public final static int COMPARE_OP = 26; /* must be even! */
public final static int EQUALS_OP = 26; public final static int EQUALS_OP = 26;
public final static int NOTEQUALS_OP = 27; public final static int NOTEQUALS_OP = 27;
public final static int LESS_OP = 28;
public final static int GREATEREQ_OP = 29;
public final static int GREATER_OP = 30;
public final static int LESSEQ_OP = 31;
public final static int LOG_AND_OP = 32; /* must be even! */ public final static int LOG_AND_OP = 32; /* must be even! */
public final static int LOG_OR_OP = 33; public final static int LOG_OR_OP = 33;
public final static int LOG_NOT_OP = 34; public final static int LOG_NOT_OP = 34;
@ -47,73 +53,271 @@ public abstract class Operator extends Expression {
"!", "~", "-" "!", "~", "-"
}; };
protected int operator; protected int operatorIndex;
private int operandcount;
Operator (Type type, int op) { Expression[] subExpressions;
public Operator(Type type) {
this(type,0);
}
public Operator(Type type, int op) {
super(type); super(type);
this.operator = op; this.operatorIndex = op;
if (type == null) if (type == null)
throw new jode.AssertError("type == null"); throw new jode.AssertError("type == null");
} }
public void initOperands(int opcount) {
operandcount = opcount;
subExpressions = new Expression[opcount];
for (int i=0; i < opcount; i++) {
subExpressions[i] = new NopOperator(Type.tUnknown);
subExpressions[i].parent = this;
}
updateSubTypes();
}
public int getFreeOperandCount() {
return operandcount;
}
/**
* Tells if this is an operator, that doesn't have any
* subExpression, yet.
*/
public boolean isFreeOperator() {
return subExpressions.length == 0
|| subExpressions[subExpressions.length-1] instanceof NopOperator;
}
/**
* Tells if this is an operator, that doesn't have any
* subExpression, yet, and that expects opcount operands
*/
public boolean isFreeOperator(int opcount) {
return subExpressions.length == opcount
&& (opcount == 0
|| subExpressions[opcount-1] instanceof NopOperator);
}
public Expression addOperand(Expression op) { public Expression addOperand(Expression op) {
return new ComplexExpression for (int i= subExpressions.length; i-- > 0;) {
(this, new Expression[getOperandCount()]).addOperand(op); int opcount = subExpressions[i].getFreeOperandCount();
if (opcount > 0) {
subExpressions[i] = subExpressions[i].addOperand(op);
operandcount
+= subExpressions[i].getFreeOperandCount() - opcount;
updateType();
return this;
}
}
throw new jode.AssertError("addOperand called, but no operand needed");
} }
public Operator getOperator() { public Operator getOperator() {
return this; return this;
} }
public Expression[] getSubExpressions() {
return subExpressions;
}
public void setSubExpressions(int i, Expression expr) {
int diff = expr.getFreeOperandCount()
- subExpressions[i].getFreeOperandCount();
subExpressions[i] = expr;
expr.parent = this;
for (Operator ce = this; ce != null; ce = (Operator) ce.parent)
ce.operandcount += diff;
updateType();
}
public int getOperatorIndex() { public int getOperatorIndex() {
return operator; return operatorIndex;
} }
public void setOperatorIndex(int op) { public void setOperatorIndex(int op) {
operator = op; operatorIndex = op;
} }
public String getOperatorString() { public String getOperatorString() {
return opString[operator]; return opString[operatorIndex];
}
public boolean opEquals(Operator o) {
return this == o;
}
public Expression simplify() {
for (int i=0; i< subExpressions.length; i++) {
subExpressions[i] = subExpressions[i].simplify();
subExpressions[i].parent = this;
}
return this;
}
public void fillInGenSet(VariableSet in, VariableSet gen) {
if (this instanceof LocalVarOperator) {
LocalVarOperator varOp = (LocalVarOperator) this;
if (varOp.isRead() && in != null)
in.addElement(varOp.getLocalInfo());
if (gen != null)
gen.addElement(varOp.getLocalInfo());
}
for (int i=0; i< subExpressions.length; i++)
subExpressions[i].fillInGenSet(in,gen);
} }
/** /**
* Get priority of the operator. * Checks if the value of the given expression can change, due to
* Currently this priorities are known: * side effects in this expression. If this returns false, the
* <ul><li> 1000 constant * expression can safely be moved behind the current expresion.
* </li><li> 950 new, .(field access), [] * @param expr the expression that should not change.
* </li><li> 900 new[]
* </li><li> 800 ++,-- (post)
* </li><li> 700 ++,--(pre), +,-(unary), ~, !, cast
* </li><li> 650 *,/, %
* </li><li> 610 +,-
* </li><li> 600 <<, >>, >>>
* </li><li> 550 >, <, >=, <=, instanceof
* </li><li> 500 ==, !=
* </li><li> 450 &
* </li><li> 420 ^
* </li><li> 410 |
* </li><li> 350 &&
* </li><li> 310 ||
* </li><li> 200 ?:
* </li><li> 100 =, +=, -=, etc.
* </li></ul>
*/ */
public abstract int getPriority(); public boolean hasSideEffects(Expression expr) {
if (expr instanceof MatchableOperator
&& expr.containsConflictingLoad((MatchableOperator)expr))
return true;
for (int i=0; i < subExpressions.length; i++) {
if (subExpressions[i].hasSideEffects(expr))
return true;
}
return false;
}
public abstract Type getOperandType(int i); /**
public abstract int getOperandCount(); * Checks if this expression contains a conflicting load, that
public abstract void setOperandType(Type[] inputTypes); * matches the given CombineableOperator. The sub expressions are
* not checked.
* @param op The combineable operator.
* @return if this expression contains a matching load.
*/
public boolean containsConflictingLoad(MatchableOperator op) {
if (op.matches(this))
return true;
for (int i=0; i < subExpressions.length; i++) {
if (subExpressions[i].containsConflictingLoad(op))
return true;
}
return false;
}
public abstract void dumpExpression /**
(TabbedPrintWriter writer, Expression[] operands) * Checks if this expression contains a matching load, that matches the
throws java.io.IOException; * given Expression.
* @param comb The store expression.
* @return true, iff this expression contains a matching load.
* @exception ClassCastException, if e.getOperator
* is not a CombineableOperator.
*/
public boolean containsMatchingLoad(CombineableOperator comb) {
Operator combOp = (Operator) comb;
if (comb.getLValue().matches(this)) {
if (subsEquals(comb.getLValue()))
return true;
}
for (int i=0; i < subExpressions.length; i++) {
if (subExpressions[i].containsMatchingLoad(comb))
return true;
}
return false;
}
/**
* Checks if the given Expression (which must be a CombineableOperator)
* can be combined into this expression.
* @param e The store expression, must be of type void.
* @return 1, if it can, 0, if no match was found and -1, if a
* conflict was found. You may wish to check for >0.
* @exception ClassCastException, if e.getOperator
* is not a CombineableOperator.
*/
public int canCombine(CombineableOperator combOp) {
// GlobalOptions.err.println("Try to combine "+e+" into "+this);
if (combOp.getLValue() instanceof LocalStoreOperator
&& ((Operator)combOp).getFreeOperandCount() == 0) {
// Special case for locals created on inlining methods, which may
// combine everywhere, as long as there are no side effects.
for (int i=0; i < subExpressions.length; i++) {
int result = subExpressions[i].canCombine(combOp);
if (result != 0)
return result;
if (subExpressions[i].hasSideEffects((Expression)combOp))
return -1;
}
}
public void dumpExpression(TabbedPrintWriter writer) if (combOp.lvalueMatches(this))
throws java.io.IOException { return subsEquals((Operator)combOp) ? 1 : -1;
Expression[] operands = new Expression[getOperandCount()]; if (subExpressions.length > 0)
for (int i=0; i< operands.length; i++) return subExpressions[0].canCombine(combOp);
operands[i] = new NopOperator(getOperandType(i)); return 0;
dumpExpression(writer, operands);
} }
/**
* Combines the given Expression (which should be a StoreInstruction)
* into this expression. You must only call this if
* canCombine returns the value 1.
* @param e The store expression.
* @return The combined expression.
* @exception ClassCastException, if e.getOperator
* is not a CombineableOperator.
*/
public Expression combine(CombineableOperator comb) {
Operator combOp = (Operator) comb;
if (comb.lvalueMatches(this)) {
/* We already checked in canCombine that subExpressions match */
comb.makeNonVoid();
combOp.parent = parent;
return combOp;
}
for (int i=0; i < subExpressions.length; i++) {
Expression combined = subExpressions[i].combine(comb);
if (combined != null) {
subExpressions[i] = combined;
updateType();
return this;
}
}
return null;
}
public boolean subsEquals(Operator other) {
if (this == other)
return true;
if (other.subExpressions == null)
return (subExpressions == null);
if (subExpressions.length != other.subExpressions.length)
return false;
for (int i=0; i<subExpressions.length; i++) {
if(!subExpressions[i].equals(other.subExpressions[i]))
return false;
}
return true;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Operator))
return false;
Operator other = (Operator) o;
return opEquals(other) && subsEquals(other);
}
public boolean isConstant() {
for (int i=0; i< subExpressions.length; i++)
if (!subExpressions[i].isConstant())
return false;
return true;
}
public abstract void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException;
} }

@ -21,24 +21,28 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class PopOperator extends SimpleOperator { public class PopOperator extends Operator {
Type popType;
public PopOperator(Type argtype) { public PopOperator(Type argtype) {
super(Type.tVoid, 0, 1); super(Type.tVoid, 0);
operandTypes[0] = argtype; popType = argtype;
initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 0; return 0;
} }
public int getOperandPriority(int i) { public void updateSubTypes() {
return 0; subExpressions[0].setType(Type.tSubType(popType));
}
public void updateType() {
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 0); subExpressions[0].dumpExpression(writer, 0);
} }
} }

@ -21,56 +21,36 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
/**
* A PrePostFixOperator has one subexpression, namely the StoreInstruction.
*/
public class PrePostFixOperator extends Operator { public class PrePostFixOperator extends Operator {
StoreInstruction store;
boolean postfix; boolean postfix;
public PrePostFixOperator(Type type, int op, public PrePostFixOperator(Type type, int operatorIndex,
StoreInstruction store, boolean postfix) { LValueExpression lvalue, boolean postfix) {
super(type, op); super(type);
this.store = store;
this.postfix = postfix; this.postfix = postfix;
setOperatorIndex(operatorIndex);
initOperands(1);
setSubExpressions(0, lvalue);
} }
public int getPriority() { public int getPriority() {
return postfix ? 800 : 700; return postfix ? 800 : 700;
} }
public Type getOperandType(int i) { public void updateSubTypes() {
return store.getLValueOperandType(i); if (!isVoid())
} subExpressions[0].setType(type);
public int getOperandCount() {
return store.getLValueOperandCount();
}
/**
* Checks if the value of the given expression can change, due to
* side effects in this expression. If this returns false, the
* expression can safely be moved behind the current expresion.
* @param expr the expression that should not change.
*/
public boolean hasSideEffects(Expression expr) {
return store.hasSideEffects(expr);
}
/**
* Sets the return type of this operator.
*/
public void setType(Type type) {
if (!isVoid()) {
store.setLValueType(type);
super.setType(store.getLValueType());
} else
super.setType(type);
} }
public void setOperandType(Type[] inputTypes) { public void updateType() {
store.setLValueOperandType(inputTypes); if (!isVoid())
updateParentType(subExpressions[0].getType());
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
boolean needBrace = false; boolean needBrace = false;
int priority = 700; int priority = 700;
@ -78,13 +58,7 @@ public class PrePostFixOperator extends Operator {
writer.print(getOperatorString()); writer.print(getOperatorString());
priority = 800; priority = 800;
} }
if (store.getLValuePriority() < priority) { subExpressions[0].dumpExpression(writer, priority);
needBrace = true;
writer.print("(");
}
store.dumpLValue(writer, operands);
if (needBrace)
writer.print(")");
if (postfix) if (postfix)
writer.print(getOperatorString()); writer.print(getOperatorString());
} }

@ -26,7 +26,7 @@ import jode.decompiler.FieldAnalyzer;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
import jode.decompiler.Scope; import jode.decompiler.Scope;
public class PutFieldOperator extends StoreInstruction { public class PutFieldOperator extends LValueExpression {
CodeAnalyzer codeAnalyzer; CodeAnalyzer codeAnalyzer;
boolean staticFlag; boolean staticFlag;
Reference ref; Reference ref;
@ -34,13 +34,14 @@ public class PutFieldOperator extends StoreInstruction {
public PutFieldOperator(CodeAnalyzer codeAnalyzer, boolean staticFlag, public PutFieldOperator(CodeAnalyzer codeAnalyzer, boolean staticFlag,
Reference ref) { Reference ref) {
super(Type.tType(ref.getType()), ASSIGN_OP); super(Type.tType(ref.getType()));
this.codeAnalyzer = codeAnalyzer; this.codeAnalyzer = codeAnalyzer;
this.staticFlag = staticFlag; this.staticFlag = staticFlag;
this.ref = ref; this.ref = ref;
this.classType = Type.tType(ref.getClazz()); this.classType = Type.tType(ref.getClazz());
if (staticFlag) if (staticFlag)
codeAnalyzer.useType(classType); codeAnalyzer.useType(classType);
initOperands(staticFlag ? 0 : 1);
} }
public boolean isStatic() { public boolean isStatic() {
@ -76,24 +77,23 @@ public class PutFieldOperator extends StoreInstruction {
&& ((GetFieldOperator)loadop).ref.equals(ref); && ((GetFieldOperator)loadop).ref.equals(ref);
} }
public int getLValuePriority() { public int getPriority() {
return 950; return 950;
} }
public int getLValueOperandCount() { public void updateSubTypes() {
return staticFlag?0:1; if (!staticFlag)
subExpressions[0].setType(Type.tSubType(classType));
} }
public Type getLValueOperandType(int i) { public void updateType() {
return classType; updateParentType(getFieldType());
} }
public void setLValueOperandType(Type[] t) { public void dumpExpression(TabbedPrintWriter writer)
}
public void dumpLValue(TabbedPrintWriter writer, Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
boolean opIsThis = !staticFlag && operands[0] instanceof ThisOperator; boolean opIsThis = !staticFlag
&& subExpressions[0] instanceof ThisOperator;
String fieldName = ref.getName(); String fieldName = ref.getName();
if (staticFlag) { if (staticFlag) {
if (!classType.equals(Type.tClass(codeAnalyzer.getClazz())) if (!classType.equals(Type.tClass(codeAnalyzer.getClazz()))
@ -102,38 +102,42 @@ public class PutFieldOperator extends StoreInstruction {
writer.print("."); writer.print(".");
} }
writer.print(fieldName); writer.print(fieldName);
} else if (operands[0].getType() instanceof NullType) { } else if (subExpressions[0].getType() instanceof NullType) {
writer.print("(("); writer.print("((");
writer.printType(classType); writer.printType(classType);
writer.print(")"); writer.print(")");
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
writer.print(")."); writer.print(").");
writer.print(fieldName); writer.print(fieldName);
} else { } else {
if (opIsThis) { if (opIsThis) {
ThisOperator thisOp = (ThisOperator) operands[0]; ThisOperator thisOp = (ThisOperator) subExpressions[0];
Scope scope = writer.getScope(thisOp.getClassInfo(), Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE); Scope.CLASSSCOPE);
if (scope == null)
writer.println("UNKNOWN ");
if (scope == null || writer.conflicts(fieldName, scope, if (scope == null || writer.conflicts(fieldName, scope,
Scope.FIELDNAME)) { Scope.FIELDNAME)) {
thisOp.dumpExpression(writer, 950); thisOp.dumpExpression(writer, 950);
writer.print("."); writer.print(".");
} else if (writer.conflicts(fieldName, scope, } else if (writer.conflicts(fieldName, scope,
Scope.AMBIGUOUSNAME)) { Scope.AMBIGUOUSNAME)
|| (/* This is a inherited field conflicting
* with a field name in some outer class.
*/
getField() == null
&& writer.conflicts(fieldName, null,
Scope.FIELDNAME))) {
writer.print("this."); writer.print("this.");
} }
} else { } else {
operands[0].dumpExpression(writer, 950); subExpressions[0].dumpExpression(writer, 950);
writer.print("."); writer.print(".");
} }
writer.print(fieldName); writer.print(fieldName);
} }
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return o instanceof PutFieldOperator return o instanceof PutFieldOperator
&& ((PutFieldOperator)o).ref.equals(ref); && ((PutFieldOperator)o).ref.equals(ref);
} }

@ -29,11 +29,12 @@ public class ShiftOperator extends BinaryOperator {
super(type, op); super(type, op);
} }
public Type getOperandType(int i) { public void updateSubTypes() {
return (i==0) ? type : Type.tInt; subExpressions[0].setType(Type.tSubType(type));
subExpressions[1].setType(Type.tSubType(Type.tInt));
} }
public void setOperandType(Type[] inputTypes) { public void updateType() {
setType(inputTypes[0]); updateParentType(Type.tSuperType(subExpressions[0].getType()));
} }
} }

@ -21,28 +21,10 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
public abstract class SimpleOperator extends Operator { public abstract class SimpleOperator extends Operator {
protected Type[] operandTypes;
public SimpleOperator(Type type, int operator, public SimpleOperator(Type type, int operator,
int operandCount) { int operandCount) {
super(type, operator); super(type, operator);
operandTypes = new Type[operandCount]; initOperands(operandCount);
for (int i=0; i< operandCount; i++) {
operandTypes[i] = type;
}
}
public int getOperandCount() {
return operandTypes.length;
}
public Type getOperandType(int i) {
return operandTypes[i];
}
public void setOperandType(Type[] t) {
for (int i=0; i< operandTypes.length; i++) {
operandTypes[i] = operandTypes[i].intersection(t[i]);
}
} }
} }

@ -19,42 +19,29 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public abstract class StoreInstruction extends Operator public class StoreInstruction extends Operator
implements CombineableOperator { implements CombineableOperator {
public String lvCasts; boolean isOpAssign = false;
Type lvalueType;
Type rvalueType = null;
public StoreInstruction(Type type, int operator) { public StoreInstruction(LValueExpression lvalue) {
super(Type.tVoid, operator); super(Type.tVoid, ASSIGN_OP);
lvalueType = type; initOperands(2);
lvCasts = lvalueType.toString(); setSubExpressions(0, lvalue);
} }
public void makeOpAssign(int operator, Type rvalueType) { public LValueExpression getLValue() {
setOperatorIndex(operator); return (LValueExpression) subExpressions[0];
this.rvalueType = rvalueType;
} }
public Type getType() { public void makeOpAssign(int operatorIndex) {
return type == Type.tVoid ? type : getLValueType(); setOperatorIndex(operatorIndex);
} if (subExpressions[1] instanceof NopOperator)
subExpressions[1].type = Type.tUnknown;
public Type getLValueType() { isOpAssign = true;
return lvalueType;
}
/**
* Checks if the value of the given expression can change, due to
* side effects in this expression. If this returns false, the
* expression can safely be moved behind the current expresion.
* @param expr the expression that should not change.
*/
public boolean hasSideEffects(Expression expr) {
return expr.containsConflictingLoad(this);
} }
/** /**
@ -63,64 +50,70 @@ public abstract class StoreInstruction extends Operator
public void makeNonVoid() { public void makeNonVoid() {
if (type != Type.tVoid) if (type != Type.tVoid)
throw new jode.AssertError("already non void"); throw new jode.AssertError("already non void");
type = lvalueType; type = subExpressions[0].getType();
if (parent != null && parent.getOperator() == this)
parent.type = lvalueType;
} }
public abstract boolean matches(Operator loadop); public boolean lvalueMatches(Operator loadop) {
public abstract int getLValueOperandCount(); return getLValue().matches(loadop);
public abstract Type getLValueOperandType(int i);
public abstract void setLValueOperandType(Type [] t);
/**
* Sets the type of the lvalue (and rvalue).
*/
public void setLValueType(Type type) {
lvalueType = lvalueType.intersection(type);
} }
public int getPriority() { public int getPriority() {
return 100; return 100;
} }
public abstract int getLValuePriority(); public void updateSubTypes() {
if (!isVoid()) {
public Type getOperandType(int i) { subExpressions[0].setType(type);
if (i == getLValueOperandCount()) { subExpressions[1].setType(Type.tSubType(type));
if (getOperatorIndex() == ASSIGN_OP) }
/* In a direct assignment, lvalueType is rvalueType */
return getLValueType();
else
return rvalueType;
} else
return getLValueOperandType(i);
} }
public void setOperandType(Type[] t) { public void updateType() {
int count = getLValueOperandCount();
if (count > 0) Type newType;
setLValueOperandType(t);
if (getOperatorIndex() == ASSIGN_OP) if (!isOpAssign) {
/* In a direct assignment, lvalueType is rvalueType */ /* An opassign (+=, -=, etc.) doesn't merge rvalue type. */
setLValueType(t[count]); Type lvalueType = subExpressions[0].getType();
else Type rvalueType = subExpressions[1].getType();
rvalueType = rvalueType.intersection(t[count]); subExpressions[0].setType(Type.tSuperType(rvalueType));
subExpressions[1].setType(Type.tSubType(lvalueType));
}
if (!isVoid())
updateParentType(subExpressions[0].getType());
} }
public int getOperandCount() { public Expression simplify() {
return 1 + getLValueOperandCount(); if (subExpressions[1] instanceof ConstOperator) {
ConstOperator one = (ConstOperator) subExpressions[1];
if ((getOperatorIndex() == OPASSIGN_OP+ADD_OP ||
getOperatorIndex() == OPASSIGN_OP+SUB_OP) &&
(one.getValue().equals("1")
|| one.getValue().equals("1.0"))) {
int op = (getOperatorIndex() == OPASSIGN_OP+ADD_OP)
? INC_OP : DEC_OP;
return new PrePostFixOperator
(getType(), op, getLValue(), isVoid()).simplify();
}
}
return super.simplify();
} }
public abstract void dumpLValue(TabbedPrintWriter writer, public boolean opEquals(Operator o) {
Expression[] operands) return o instanceof StoreInstruction
throws java.io.IOException; && o.operatorIndex == operatorIndex
&& o.isVoid() == isVoid();
}
public void dumpExpression(TabbedPrintWriter writer, Expression[] operands) public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException throws java.io.IOException
{ {
dumpLValue(writer, operands); subExpressions[0].dumpExpression(writer, 950);
writer.print(getOperatorString()); writer.print(getOperatorString());
operands[getLValueOperandCount()].dumpExpression(writer, 100); subExpressions[1].dumpExpression(writer, 100);
} }
} }

@ -21,31 +21,40 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class StringAddOperator extends SimpleOperator { public class StringAddOperator extends Operator {
protected Type operandType; protected Type operandType;
public StringAddOperator() { public StringAddOperator() {
super(Type.tString, ADD_OP, 2); super(Type.tString, ADD_OP);
operandTypes[1] = Type.tUnknown; initOperands(2);
} }
public void clearFirstType() {
operandTypes[0] = Type.tUnknown;
}
public int getPriority() { public int getPriority() {
return 610; return 610;
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return (o instanceof StringAddOperator); return (o instanceof StringAddOperator);
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateSubTypes() {
Expression[] operands) /* A string add allows everything */
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
operands[0].dumpExpression(writer, 610);
if (!subExpressions[0].getType().isOfType(Type.tString)
&& !subExpressions[1].getType().isOfType(Type.tString))
writer.print("\"\" + ");
subExpressions[0].dumpExpression(writer, 610);
writer.print(getOperatorString()); writer.print(getOperatorString());
operands[1].dumpExpression(writer, 611); subExpressions[1].dumpExpression(writer, 611);
} }
} }

@ -20,6 +20,7 @@
package jode.expr; package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.bytecode.ClassInfo; import jode.bytecode.ClassInfo;
import jode.decompiler.Scope;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class ThisOperator extends NoArgOperator { public class ThisOperator extends NoArgOperator {
@ -48,19 +49,18 @@ public class ThisOperator extends NoArgOperator {
return classInfo+".this"; return classInfo+".this";
} }
public boolean equals(Object o) { public boolean opEquals(Operator o) {
return (o instanceof ThisOperator && return (o instanceof ThisOperator &&
((ThisOperator) o).classInfo.equals(classInfo)); ((ThisOperator) o).classInfo.equals(classInfo));
} }
public void dumpExpression(TabbedPrintWriter writer, public void dumpExpression(TabbedPrintWriter writer)
Expression[] operands)
throws java.io.IOException { throws java.io.IOException {
if (!isInnerMost) { if (!isInnerMost) {
writer.print(writer.getClassString(classInfo)); writer.print(writer.getClassString(classInfo,
Scope.AMBIGUOUSNAME));
writer.print("."); writer.print(".");
} }
writer.print("this"); writer.print("this");
} }
} }

@ -21,33 +21,42 @@ package jode.expr;
import jode.type.Type; import jode.type.Type;
import jode.decompiler.TabbedPrintWriter; import jode.decompiler.TabbedPrintWriter;
public class UnaryOperator extends SimpleOperator { public class UnaryOperator extends Operator {
public UnaryOperator(Type type, int op) { public UnaryOperator(Type type, int op) {
super(type, op, 1); super(type, op);
initOperands(1);
} }
public int getPriority() { public int getPriority() {
return 700; return 700;
} }
/** public Expression negate() {
* Sets the return type of this operator. if (getOperatorIndex() == LOG_NOT_OP) {
*/ if (subExpressions != null)
public void setType(Type type) { return subExpressions[0];
super.setType(type); else
Type newOpType = type.intersection(operandTypes[0]); return new NopOperator(Type.tBoolean);
operandTypes[0] = newOpType; }
return super.negate();
} }
public boolean equals(Object o) { public void updateSubTypes() {
return (o instanceof UnaryOperator) && subExpressions[0].setType(Type.tSubType(type));
((UnaryOperator)o).operator == operator;
} }
public void dumpExpression(TabbedPrintWriter writer, public void updateType() {
Expression[] operands) updateParentType(Type.tSuperType(subExpressions[0].getType()));
}
public boolean opEquals(Operator o) {
return (o instanceof UnaryOperator)
&& o.operatorIndex == operatorIndex;
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException { throws java.io.IOException {
writer.print(getOperatorString()); writer.print(getOperatorString());
operands[0].dumpExpression(writer, 700); subExpressions[0].dumpExpression(writer, 700);
} }
} }

Loading…
Cancel
Save