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 {
Type arrayType;
public ArrayLengthOperator() {
super(Type.tInt, 0);
arrayType = Type.tArray(Type.tUnknown);
initOperands(1);
}
public int getPriority() {
return 950;
}
public int getOperandCount() {
return 1;
}
public Type getOperandType(int i) {
return arrayType;
public void updateSubTypes() {
subExpressions[0].setType(Type.tArray(Type.tUnknown));
}
public void setOperandType(Type[] types) {
arrayType = arrayType.intersection(types[0]);
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
operands[0].dumpExpression(writer, 900);
subExpressions[0].dumpExpression(writer, 900);
writer.print(".length");
}
}

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

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

@ -21,14 +21,15 @@ package jode.expr;
import jode.type.Type;
import jode.decompiler.TabbedPrintWriter;
public class BinaryOperator extends SimpleOperator {
public class BinaryOperator extends Operator {
public BinaryOperator(Type type, int op) {
super(type, op, 2);
super(type, op);
initOperands(2);
}
public int getPriority() {
switch (operator) {
switch (operatorIndex) {
case 1: case 2:
return 610;
case 3: case 4: case 5:
@ -52,28 +53,42 @@ public class BinaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator");
}
public int getOperandPriority(int i) {
return getPriority() + i;
public void updateSubTypes() {
subExpressions[0].setType(Type.tSubType(type));
subExpressions[1].setType(Type.tSubType(type));
}
public Type getOperandType(int i) {
return type;
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 */
updateParentType(leftType.intersection(rightType));
}
public void setOperandType(Type[] inputTypes) {
setType(inputTypes[0].intersection(inputTypes[1]));
public Expression negate() {
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) &&
((BinaryOperator)o).operator == operator;
o.operatorIndex == operatorIndex;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
operands[0].dumpExpression(writer, getPriority());
subExpressions[0].dumpExpression(writer, getPriority());
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.decompiler.TabbedPrintWriter;
public class CheckCastOperator extends SimpleOperator {
public class CheckCastOperator extends Operator {
Type castType;
public CheckCastOperator(Type type) {
super(type, 0, 1);
super(type, 0);
castType = type;
operandTypes[0] = Type.tUnknown;
initOperands(1);
}
public int getPriority() {
return 700;
}
public void setOperandType(Type[] type) {
super.setOperandType(type);
public void updateSubTypes() {
subExpressions[0].setType(Type.tUObject);
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
writer.print("(");
writer.printType(castType);
@ -49,12 +51,12 @@ public class CheckCastOperator extends SimpleOperator {
* to the common super type before. This cases always give a runtime
* 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) {
writer.print("(");
writer.printType(superType);
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 {
Type operandType;
LocalInfo local;
public CheckNullOperator(Type type, LocalInfo li) {
super(type, 0);
operandType = type;
local = li;
local.setType(type);
}
public int getOperandCount() {
return 1;
initOperands(1);
}
public int getPriority() {
return 200;
}
public int getOperandPriority(int i) {
return 0;
}
public Type getOperandType(int i) {
return operandType;
public void updateSubTypes() {
local.setType(type);
subExpressions[0].setType(Type.tSubType(type));
}
public void setOperandType(Type[] inputTypes) {
operandType = operandType.intersection(inputTypes[0]);
type = operandType;
local.setType(type);
public void updateType() {
Type newType = Type.tSuperType(subExpressions[0].getType())
.intersection(type);
local.setType(newType);
updateParentType(newType);
}
public void removeLocal() {
local.remove();
}
/**
* 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)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
writer.print("("+local.getName()+" = ");
operands[0].dumpExpression(writer, 0);
subExpressions[0].dumpExpression(writer, 0);
writer.print(").getClass() != null ? "+local.getName()+" : null");
}
}

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

@ -19,7 +19,15 @@
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
* that it should replace.

@ -21,10 +21,13 @@ package jode.expr;
import jode.type.Type;
import jode.decompiler.TabbedPrintWriter;
public class CompareBinaryOperator extends SimpleOperator {
public class CompareBinaryOperator extends Operator {
Type compareType;
public CompareBinaryOperator(Type type, int op) {
super(Type.tBoolean, op, 2);
operandTypes[0] = operandTypes[1] = type;
super(Type.tBoolean, op);
compareType = type;
initOperands(2);
}
public int getPriority() {
@ -41,22 +44,41 @@ public class CompareBinaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator");
}
public void setOperandType(Type[] inputTypes) {
super.setOperandType(inputTypes);
operandTypes[0] = operandTypes[1] =
operandTypes[0].intersection(operandTypes[1]);
public Type getCompareType() {
return compareType;
}
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) {
return (o instanceof CompareBinaryOperator) &&
((CompareBinaryOperator)o).operator == operator;
public boolean opEquals(Operator o) {
return (o instanceof CompareBinaryOperator)
&& o.operatorIndex == operatorIndex;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
operands[0].dumpExpression(writer, getPriority());
subExpressions[0].dumpExpression(writer, getPriority());
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.decompiler.TabbedPrintWriter;
public class CompareToIntOperator extends SimpleOperator {
public class CompareToIntOperator extends Operator {
boolean allowsNAN;
boolean greaterOnNAN;
Type compareType;
public CompareToIntOperator(Type type, boolean greaterOnNAN) {
super(Type.tInt, 0, 2);
operandTypes[0] = operandTypes[1] = type;
super(Type.tInt, 0);
compareType = type;
this.allowsNAN = (type == Type.tFloat || type == Type.tDouble);
this.greaterOnNAN = greaterOnNAN;
initOperands(2);
}
public int getPriority() {
return 499;
}
public void setOperandType(Type[] inputTypes) {
super.setOperandType(inputTypes);
Type operandType = operandTypes[0].intersection(operandTypes[1]);
operandTypes[0] = operandTypes[1] = operandType;
public void updateSubTypes() {
subExpressions[0].setType(Type.tSubType(compareType));
subExpressions[1].setType(Type.tSubType(compareType));
}
public boolean equals(Object o) {
public void updateType() {
}
public boolean opEquals(Operator o) {
return (o instanceof CompareToIntOperator);
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException
{
operands[0].dumpExpression(writer, 550);
writer.print(" <=>" + (greaterOnNAN ? 'g' : 'l') + ' ');
operands[1].dumpExpression(writer, 551);
subExpressions[0].dumpExpression(writer, 550);
writer.print(" <=>");
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.decompiler.TabbedPrintWriter;
public class CompareUnaryOperator extends SimpleOperator {
public class CompareUnaryOperator extends Operator {
boolean objectType;
Type compareType;
public CompareUnaryOperator(Type type, int op) {
super(Type.tBoolean, op, 1);
operandTypes[0] = type;
super(Type.tBoolean, op);
compareType = type;
objectType = (type.isOfType(Type.tUObject));
initOperands(1);
}
public int getPriority() {
@ -44,15 +46,69 @@ public class CompareUnaryOperator extends SimpleOperator {
throw new RuntimeException("Illegal operator");
}
public boolean equals(Object o) {
return (o instanceof CompareUnaryOperator) &&
((CompareUnaryOperator)o).operator == operator;
public Type getCompareType() {
return compareType;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateSubTypes() {
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 {
operands[0].dumpExpression(writer, getPriority());
subExpressions[0].dumpExpression(writer, getPriority());
writer.print(getOperatorString());
writer.print(objectType?"null":"0");
}

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

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

@ -18,12 +18,15 @@
*/
package jode.expr;
import java.lang.reflect.Modifier;
import jode.type.Type;
import jode.type.NullType;
import jode.type.ClassInterfacesType;
import jode.type.MethodType;
import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
import jode.bytecode.Reference;
import jode.Decompiler;
import jode.decompiler.CodeAnalyzer;
import jode.decompiler.ClassAnalyzer;
@ -38,11 +41,22 @@ public class ConstructorOperator extends Operator
ClassAnalyzer anonymousClass = null;
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) {
super(isVoid ? Type.tVoid : invoke.getClassType(), 0);
this.classType = invoke.getClassType();
this.methodType = invoke.getMethodType();
this.codeAnalyzer = invoke.codeAnalyzer;
initOperands(methodType.getParameterTypes().length);
checkAnonymousClasses();
}
@ -54,16 +68,6 @@ public class ConstructorOperator extends Operator
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.
*/
@ -77,30 +81,46 @@ public class ConstructorOperator extends Operator
return 950;
}
public int getOperandCount() {
return methodType.getParameterTypes().length;
}
public int getOperandPriority(int i) {
return 0;
}
public Type getClassType() {
return classType;
}
public Type getOperandType(int i) {
return methodType.getParameterTypes()[i];
public void updateSubTypes() {
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() {
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)
? 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() {
if (classType instanceof ClassInterfacesType) {
return ((ClassInterfacesType) classType).getClassInfo();
@ -135,8 +155,7 @@ public class ConstructorOperator extends Operator
}
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
int arg = 0;
@ -146,8 +165,10 @@ public class ConstructorOperator extends Operator
return;
}
InnerClassInfo outer = getOuterClassInfo();
if (outer != null && outer.name != null) {
Expression outExpr = operands[arg++];
if (outer != null && outer.outer != null
&& !Modifier.isStatic(outer.modifiers)
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0) {
Expression outExpr = subExpressions[arg++];
if (!removedCheckNull && !(outExpr instanceof ThisOperator))
writer.print("MISSING CHECKNULL");
if (outExpr instanceof ThisOperator) {
@ -180,10 +201,8 @@ public class ConstructorOperator extends Operator
for (int i = arg; i < methodType.getParameterTypes().length; i++) {
if (i>arg)
writer.print(", ");
operands[i].dumpExpression(writer, 0);
subExpressions[i].dumpExpression(writer, 0);
}
writer.print(")");
}
}

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

@ -21,50 +21,117 @@ package jode.expr;
import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter;
import jode.flow.VariableSet;
public abstract class Expression {
protected Type type;
Expression parent = null;
Operator parent = null;
public Expression(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() {
return type;
}
public Expression getParent() {
public Operator getParent() {
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)
parent.updateType();
}
/**
* Get priority of the operator.
* 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.
* @return The number of stack entries this expression needs.
*/
public abstract int getOperandCount();
public abstract int getFreeOperandCount();
public abstract Expression addOperand(Expression op);
public Expression negate() {
Operator negop =
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.
*/
public boolean hasSideEffects(Expression expr) {
// Most expression don't have side effects.
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
* conflict was found. You may wish to check for >0.
*/
public int canCombine(Expression e) {
// jode.GlobalOptions.err.println("Try to combine "+e+" into "+this);
return containsMatchingLoad(e)? 1 : 0;
public int canCombine(CombineableOperator combOp) {
return 0;
}
/**
* 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.
* @return if this expression contains a matching load.
* @exception ClassCastException, if e.getOperator
* is not a CombineableOperator.
*/
public boolean containsMatchingLoad(Expression e) {
return ((CombineableOperator)e.getOperator()).matches(getOperator());
public boolean containsMatchingLoad(CombineableOperator e) {
return false;
}
/**
@ -109,7 +174,7 @@ public abstract class Expression {
* @param op The combineable operator.
* @return if this expression contains a matching load. */
public boolean containsConflictingLoad(MatchableOperator op) {
return op.matches(getOperator());
return false;
}
/**
@ -122,14 +187,7 @@ public abstract class Expression {
* @exception ClassCastException, if e.getOperator
* is not a CombineableOperator.
*/
public Expression combine(Expression e) {
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;
}
public Expression combine(CombineableOperator comb) {
return null;
}
@ -158,8 +216,6 @@ public abstract class Expression {
return null;
}
public abstract Operator getOperator();
public void makeInitializer() {
}
@ -167,24 +223,33 @@ public abstract class Expression {
return true;
}
public void fillInGenSet(VariableSet in, VariableSet gen) {
}
public abstract void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException;
public void dumpExpression(TabbedPrintWriter writer, int minPriority)
throws java.io.IOException {
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) {
needParen1 = true;
writer.print("(");
}
writer.print("("+getType()+") ");
writer.print("(TYPE "+type+") ");
minPriority = 700;
} else if (getType() == Type.tError) {
writer.print("(/*type error */");
needParen1 = true;
}
if (getOperator().getPriority() < minPriority) {
if (getPriority() < minPriority) {
needParen2 = true;
writer.print("(");
}
@ -208,7 +273,6 @@ public abstract class Expression {
}
}
public boolean isVoid() {
return getType() == Type.tVoid;
}

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

@ -22,53 +22,38 @@ import jode.type.Type;
import jode.decompiler.LocalInfo;
import jode.decompiler.TabbedPrintWriter;
public class IIncOperator extends NoArgOperator
implements LocalVarOperator, CombineableOperator {
String value;
LocalInfo local;
public class IIncOperator extends Operator
implements CombineableOperator {
int value;
public IIncOperator(LocalInfo local, String value, int operator) {
public IIncOperator(LocalStoreOperator localStore, int value,
int operator) {
super(Type.tVoid, operator);
this.local = local;
this.value = value;
local.setType(Type.tUInt);
local.setOperator(this);
initOperands(1);
setSubExpressions(0, localStore);
}
public String getValue() {
return value;
}
public boolean isRead() {
return true;
}
public boolean isWrite() {
return true;
}
public void updateType() {
if (parent != null)
parent.updateType();
public LValueExpression getLValue() {
return (LValueExpression) subExpressions[0];
}
public LocalInfo getLocalInfo() {
return local;
public String getValue() {
return Integer.toString(value);
}
public int getPriority() {
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
* 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);
public void updateType() {
if (type != Type.tVoid)
updateParentType(subExpressions[0].getType());
}
/**
@ -77,29 +62,27 @@ public class IIncOperator extends NoArgOperator
public void makeNonVoid() {
if (type != Type.tVoid)
throw new jode.AssertError("already non void");
type = local.getType();
type = subExpressions[0].getType();
}
public boolean matches(Operator loadop) {
return loadop instanceof LocalLoadOperator &&
((LocalLoadOperator)loadop).getLocalInfo().getLocalInfo()
== local.getLocalInfo();
public boolean lvalueMatches(Operator loadop) {
return getLValue().matches(loadop);
}
public Expression simplify() {
if (value.equals("1")) {
if (value == 1) {
int op = (getOperatorIndex() == OPASSIGN_OP+ADD_OP)
? INC_OP : DEC_OP;
return new LocalPrePostFixOperator
(getType(), op, this, isVoid()).simplify();
return new PrePostFixOperator
(getType(), op, getLValue(), isVoid()).simplify();
}
return super.simplify();
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
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;
import jode.type.Type;
import jode.decompiler.FieldAnalyzer;
import jode.decompiler.TabbedPrintWriter;
public class IfThenElseOperator extends SimpleOperator {
public class IfThenElseOperator extends Operator {
public IfThenElseOperator(Type type) {
super(type, 0, 3);
operandTypes[0] = Type.tBoolean;
super(type, 0);
initOperands(3);
}
public int getOperandCount() {
return 3;
}
public int getPriority() {
return 200;
}
public void setOperandType(Type[] inputTypes) {
super.setOperandType(inputTypes);
Type operandType =
type.intersection(operandTypes[1]).intersection(operandTypes[2]);
type = operandTypes[1] = operandTypes[2] = operandType;
public void updateSubTypes() {
subExpressions[0].setType(Type.tBoolean);
subExpressions[1].setType(Type.tSubType(type));
subExpressions[2].setType(Type.tSubType(type));
}
public void updateType() {
Type subType = Type.tSuperType(subExpressions[1].getType())
.intersection(Type.tSuperType(subExpressions[2].getType()));
updateParentType(subType);
}
/**
* Sets the return type of this operator.
* @return true if the operand types changed
*/
public void setType(Type newType) {
Type operandType =
type.intersection(operandTypes[1]).intersection(newType);
if (!type.equals(operandType)) {
type = operandTypes[1] = operandTypes[2] = operandType;
public Expression simplify() {
if (getType().isOfType(Type.tBoolean)) {
if (subExpressions[1] instanceof ConstOperator
&& subExpressions[2] instanceof ConstOperator) {
ConstOperator c1 = (ConstOperator) subExpressions[1];
ConstOperator c2 = (ConstOperator) subExpressions[2];
if (c1.getValue().equals("1") &&
c2.getValue().equals("0"))
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);
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
operands[0].dumpExpression(writer, 201);
subExpressions[0].dumpExpression(writer, 201);
writer.print(" ? ");
operands[1].dumpExpression(writer, 0);
subExpressions[1].dumpExpression(writer, 0);
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.decompiler.TabbedPrintWriter;
public class InstanceOfOperator extends SimpleOperator {
public class InstanceOfOperator extends Operator {
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;
Type instanceType;
public InstanceOfOperator(Type type) {
super(Type.tBoolean, 0, 1);
super(Type.tBoolean, 0);
this.instanceType = type;
this.operandTypes[0] = Type.tUnknown;
}
public int getOperandCount() {
return 1;
initOperands(1);
}
public int getPriority() {
return 550;
}
public void setOperandType(Type[] type) {
super.setOperandType(type);
superType = instanceType.getCastHelper(type[0]);
public void updateSubTypes() {
subExpressions[0].setType(Type.tUObject);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
/* 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
* 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) {
writer.print("(");
writer.printType(superType);
writer.print(") ");
operands[0].dumpExpression(writer, 700);
subExpressions[0].dumpExpression(writer, 700);
} else
operands[0].dumpExpression(writer, 550);
subExpressions[0].dumpExpression(writer, 550);
writer.print(" instanceof ");
writer.printType(instanceType);
}

@ -18,6 +18,8 @@
*/
package jode.expr;
import java.lang.reflect.Modifier;
import jode.Decompiler;
import jode.decompiler.CodeAnalyzer;
import jode.decompiler.MethodAnalyzer;
@ -43,7 +45,7 @@ public final class InvokeOperator extends Operator
boolean staticFlag, boolean specialFlag,
Reference reference) {
super(Type.tUnknown, 0);
this.methodType = (MethodType) Type.tType(reference.getType());
this.methodType = Type.tMethod(reference.getType());
this.methodName = reference.getName();
this.classType = Type.tType(reference.getClazz());
this.type = methodType.getReturnType();
@ -52,25 +54,8 @@ public final class InvokeOperator extends Operator
this.specialFlag = specialFlag;
if (staticFlag)
codeAnalyzer.useType(classType);
}
/**
* 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);
initOperands((staticFlag ? 0 : 1)
+ methodType.getParameterTypes().length);
}
public final boolean isStatic() {
@ -93,21 +78,16 @@ public final class InvokeOperator extends Operator
return 950;
}
public int getOperandCount() {
return (isStatic()?0:1)
+ methodType.getParameterTypes().length;
}
public Type getOperandType(int i) {
public void updateSubTypes() {
int offset = 0;
if (!isStatic()) {
if (i == 0)
return getClassType();
i--;
subExpressions[offset++].setType(Type.tSubType(getClassType()));
}
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 setOperandType(Type types[]) {
public void updateType() {
}
public boolean isConstructor() {
@ -129,13 +109,12 @@ public final class InvokeOperator extends Operator
return getClassInfo() == codeAnalyzer.getClazz();
}
public ClassInfo getOuterClass() {
public InnerClassInfo getOuterClassInfo() {
ClassInfo clazz = getClassInfo();
if (clazz != null) {
InnerClassInfo[] outers = clazz.getOuterClasses();
if (outers != null && outers[0].outer != null
&& (Decompiler.options & Decompiler.OPTION_INNER) != 0)
return ClassInfo.forName(outers[0].outer);
if (outers != null)
return outers[0];
}
return null;
}
@ -202,90 +181,13 @@ public final class InvokeOperator extends Operator
return false;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
throws java.io.IOException {
boolean opIsThis = !staticFlag && operands[0] instanceof ThisOperator;
int arg = 1;
if (isConstructor()) {
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(")");
/**
* 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);
}
/**
@ -329,11 +231,6 @@ public final class InvokeOperator extends Operator
}
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();
CodeAnalyzer ca = clazz.getMethod(methodName, methodType).getCode();
if (ca == null)
@ -361,8 +258,89 @@ public final class InvokeOperator extends Operator
return new ConstOperator(result);
}
public Expression simplifyAccess(Expression[] subs) {
if (isOuter()) {
public Expression simplifyStringBuffer() {
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();
if (synth != null) {
Operator op = null;
@ -376,12 +354,14 @@ public final class InvokeOperator extends Operator
synth.getReference());
break;
case SyntheticAnalyzer.ACCESSPUTFIELD:
op = new PutFieldOperator(codeAnalyzer, false,
synth.getReference());
op = new StoreInstruction
(new PutFieldOperator(codeAnalyzer, false,
synth.getReference()));
break;
case SyntheticAnalyzer.ACCESSPUTSTATIC:
op = new PutFieldOperator(codeAnalyzer, true,
synth.getReference());
op = new StoreInstruction
(new PutFieldOperator(codeAnalyzer, true,
synth.getReference()));
break;
case SyntheticAnalyzer.ACCESSMETHOD:
op = new InvokeOperator(codeAnalyzer, false,
@ -394,9 +374,11 @@ public final class InvokeOperator extends Operator
}
if (op != null) {
if (subs == null)
return op;
return new ComplexExpression(op, subs);
if (subExpressions != null) {
for (int i=subExpressions.length; i-- > 0; )
op.addOperand(subExpressions[i]);
}
return op;
}
}
}
@ -404,9 +386,12 @@ public final class InvokeOperator extends Operator
}
public Expression simplify() {
Expression expr = simplifyAccess(null);
Expression expr = simplifyAccess();
if (expr != null)
return expr.simplify();
expr = simplifyString();
if (expr != this)
return expr.simplify();
return super.simplify();
}
@ -414,4 +399,102 @@ public final class InvokeOperator extends Operator
/* Invokes never equals: they may return different values even if
* 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.TabbedPrintWriter;
public class LocalLoadOperator extends NoArgOperator
public class LocalLoadOperator extends Operator
implements LocalVarOperator {
CodeAnalyzer codeAnalyzer;
LocalInfo local;
@ -35,8 +35,8 @@ public class LocalLoadOperator extends NoArgOperator
super(type);
this.codeAnalyzer = codeAnalyzer;
this.local = local;
local.setType(type);
local.setOperator(this);
initOperands(0);
}
public boolean isRead() {
@ -51,43 +51,30 @@ public class LocalLoadOperator extends NoArgOperator
return false;
}
public int getPriority() {
return 1000;
}
public LocalInfo 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() {
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_TYPES) != 0)
GlobalOptions.err.println("local "+local.getName()+" changed: "
+type+" to "+local.getType()
+" in "+parent);
super.setType(local.getType());
if (parent != null)
parent.updateType();
updateParentType(local.getType());
}
public int getPriority() {
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) {
public boolean opEquals(Operator o) {
return (o instanceof LocalLoadOperator &&
((LocalLoadOperator) o).local.getSlot() == local.getSlot());
}
@ -102,10 +89,8 @@ public class LocalLoadOperator extends NoArgOperator
return super.simplify();
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
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.TabbedPrintWriter;
public class LocalStoreOperator extends StoreInstruction
public class LocalStoreOperator extends LValueExpression
implements LocalVarOperator {
LocalInfo local;
public LocalStoreOperator(Type lvalueType, LocalInfo local, int operator) {
super(lvalueType, operator);
public LocalStoreOperator(Type lvalueType, LocalInfo local) {
super(lvalueType);
this.local = local;
local.setType(lvalueType);
local.setOperator(this);
initOperands(0);
}
public boolean isRead() {
return operator != ASSIGN_OP;
return parent != null && parent.getOperatorIndex() != ASSIGN_OP;
}
public boolean isWrite() {
return true;
}
public void updateSubTypes() {
local.setType(type);
}
public void updateType() {
if (parent != null)
parent.updateType();
updateParentType(local.getType());
}
public LocalInfo getLocalInfo() {
return local.getLocalInfo();
}
public Type getLValueType() {
return local.getType();
}
public void setLValueType(Type type) {
local.setType(type);
}
public boolean matches(Operator loadop) {
return loadop instanceof LocalLoadOperator &&
((LocalLoadOperator)loadop).getLocalInfo().getSlot()
== local.getSlot();
}
public int getLValuePriority() {
public int getPriority() {
return 1000;
}
public int getLValueOperandCount() {
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) {
public void dumpExpression(TabbedPrintWriter writer) {
writer.print(local.getName());
}
}

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

@ -21,20 +21,26 @@ package jode.expr;
import jode.type.Type;
import jode.decompiler.TabbedPrintWriter;
public class MonitorEnterOperator extends SimpleOperator {
public class MonitorEnterOperator extends Operator {
public MonitorEnterOperator() {
super(Type.tVoid, 0, 1);
operandTypes[0] = Type.tObject;
super(Type.tVoid, 0);
initOperands(1);
}
public int getPriority() {
return 700;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateSubTypes() {
subExpressions[0].setType(Type.tUObject);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
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.decompiler.TabbedPrintWriter;
public class MonitorExitOperator extends SimpleOperator {
public class MonitorExitOperator extends Operator {
public MonitorExitOperator() {
super(Type.tVoid, 0, 1);
operandTypes[0] = Type.tObject;
super(Type.tVoid, 0);
initOperands(1);
}
public int getPriority() {
return 700;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateSubTypes() {
subExpressions[0].setType(Type.tUObject);
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
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.decompiler.TabbedPrintWriter;
public class NewArrayOperator extends SimpleOperator {
public class NewArrayOperator extends Operator {
String baseTypeString;
public NewArrayOperator(Type arrayType, int dimensions) {
super(arrayType, 0, dimensions);
for (int i=0; i< dimensions; i++) {
operandTypes[i] = Type.tUInt;
}
super(arrayType, 0);
initOperands(dimensions);
}
public int getDimensions() {
return subExpressions.length;
}
public int getPriority() {
return 900;
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateSubTypes() {
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 {
Type flat = type;
int depth = 0;
@ -49,8 +58,8 @@ public class NewArrayOperator extends SimpleOperator {
writer.printType(flat);
for (int i=0; i< depth; i++) {
writer.print("[");
if (i < getOperandCount())
operands[i].dumpExpression(writer, 0);
if (i < subExpressions.length)
subExpressions[i].dumpExpression(writer, 0);
writer.print("]");
}
}

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

@ -26,25 +26,15 @@ public abstract class NoArgOperator extends Operator {
public NoArgOperator(Type type, int operator) {
super(type, operator);
initOperands(0);
}
public NoArgOperator(Type type) {
this(type, 0);
}
public int getOperandCount() {
return 0;
public void updateType() {
}
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");
public void updateSubTypes() {
}
}

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

@ -19,7 +19,9 @@
package jode.expr;
import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter;
import jode.flow.VariableSet;
public abstract class Operator extends Expression {
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 EQUALS_OP = 26;
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_OR_OP = 33;
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);
this.operator = op;
this.operatorIndex = op;
if (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) {
return new ComplexExpression
(this, new Expression[getOperandCount()]).addOperand(op);
for (int i= subExpressions.length; i-- > 0;) {
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() {
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() {
return operator;
return operatorIndex;
}
public void setOperatorIndex(int op) {
operator = op;
operatorIndex = op;
}
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.
* 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>
* 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 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();
public abstract void setOperandType(Type[] inputTypes);
/**
* Checks if this expression contains a conflicting load, that
* 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)
throws java.io.IOException;
/**
* Checks if this expression contains a matching load, that matches the
* 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)
throws java.io.IOException {
Expression[] operands = new Expression[getOperandCount()];
for (int i=0; i< operands.length; i++)
operands[i] = new NopOperator(getOperandType(i));
dumpExpression(writer, operands);
if (combOp.lvalueMatches(this))
return subsEquals((Operator)combOp) ? 1 : -1;
if (subExpressions.length > 0)
return subExpressions[0].canCombine(combOp);
return 0;
}
/**
* 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.decompiler.TabbedPrintWriter;
public class PopOperator extends SimpleOperator {
public class PopOperator extends Operator {
Type popType;
public PopOperator(Type argtype) {
super(Type.tVoid, 0, 1);
operandTypes[0] = argtype;
super(Type.tVoid, 0);
popType = argtype;
initOperands(1);
}
public int getPriority() {
return 0;
}
public int getOperandPriority(int i) {
return 0;
public void updateSubTypes() {
subExpressions[0].setType(Type.tSubType(popType));
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
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.decompiler.TabbedPrintWriter;
/**
* A PrePostFixOperator has one subexpression, namely the StoreInstruction.
*/
public class PrePostFixOperator extends Operator {
StoreInstruction store;
boolean postfix;
public PrePostFixOperator(Type type, int op,
StoreInstruction store, boolean postfix) {
super(type, op);
this.store = store;
public PrePostFixOperator(Type type, int operatorIndex,
LValueExpression lvalue, boolean postfix) {
super(type);
this.postfix = postfix;
setOperatorIndex(operatorIndex);
initOperands(1);
setSubExpressions(0, lvalue);
}
public int getPriority() {
return postfix ? 800 : 700;
}
public Type getOperandType(int i) {
return store.getLValueOperandType(i);
}
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 updateSubTypes() {
if (!isVoid())
subExpressions[0].setType(type);
}
public void setOperandType(Type[] inputTypes) {
store.setLValueOperandType(inputTypes);
public void updateType() {
if (!isVoid())
updateParentType(subExpressions[0].getType());
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
boolean needBrace = false;
int priority = 700;
@ -78,13 +58,7 @@ public class PrePostFixOperator extends Operator {
writer.print(getOperatorString());
priority = 800;
}
if (store.getLValuePriority() < priority) {
needBrace = true;
writer.print("(");
}
store.dumpLValue(writer, operands);
if (needBrace)
writer.print(")");
subExpressions[0].dumpExpression(writer, priority);
if (postfix)
writer.print(getOperatorString());
}

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

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

@ -21,28 +21,10 @@ package jode.expr;
import jode.type.Type;
public abstract class SimpleOperator extends Operator {
protected Type[] operandTypes;
public SimpleOperator(Type type, int operator,
int operandCount) {
super(type, operator);
operandTypes = new Type[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]);
}
initOperands(operandCount);
}
}

@ -19,42 +19,29 @@
package jode.expr;
import jode.type.Type;
import jode.GlobalOptions;
import jode.decompiler.TabbedPrintWriter;
public abstract class StoreInstruction extends Operator
public class StoreInstruction extends Operator
implements CombineableOperator {
public String lvCasts;
Type lvalueType;
Type rvalueType = null;
boolean isOpAssign = false;
public StoreInstruction(Type type, int operator) {
super(Type.tVoid, operator);
lvalueType = type;
lvCasts = lvalueType.toString();
public StoreInstruction(LValueExpression lvalue) {
super(Type.tVoid, ASSIGN_OP);
initOperands(2);
setSubExpressions(0, lvalue);
}
public void makeOpAssign(int operator, Type rvalueType) {
setOperatorIndex(operator);
this.rvalueType = rvalueType;
public LValueExpression getLValue() {
return (LValueExpression) subExpressions[0];
}
public Type getType() {
return type == Type.tVoid ? type : getLValueType();
}
public Type getLValueType() {
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);
public void makeOpAssign(int operatorIndex) {
setOperatorIndex(operatorIndex);
if (subExpressions[1] instanceof NopOperator)
subExpressions[1].type = Type.tUnknown;
isOpAssign = true;
}
/**
@ -63,64 +50,70 @@ public abstract class StoreInstruction extends Operator
public void makeNonVoid() {
if (type != Type.tVoid)
throw new jode.AssertError("already non void");
type = lvalueType;
if (parent != null && parent.getOperator() == this)
parent.type = lvalueType;
type = subExpressions[0].getType();
}
public abstract boolean matches(Operator loadop);
public abstract int getLValueOperandCount();
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 boolean lvalueMatches(Operator loadop) {
return getLValue().matches(loadop);
}
public int getPriority() {
return 100;
}
public abstract int getLValuePriority();
public Type getOperandType(int i) {
if (i == getLValueOperandCount()) {
if (getOperatorIndex() == ASSIGN_OP)
/* In a direct assignment, lvalueType is rvalueType */
return getLValueType();
else
return rvalueType;
} else
return getLValueOperandType(i);
public void updateSubTypes() {
if (!isVoid()) {
subExpressions[0].setType(type);
subExpressions[1].setType(Type.tSubType(type));
}
}
public void setOperandType(Type[] t) {
int count = getLValueOperandCount();
if (count > 0)
setLValueOperandType(t);
if (getOperatorIndex() == ASSIGN_OP)
/* In a direct assignment, lvalueType is rvalueType */
setLValueType(t[count]);
else
rvalueType = rvalueType.intersection(t[count]);
public void updateType() {
Type newType;
if (!isOpAssign) {
/* An opassign (+=, -=, etc.) doesn't merge rvalue type. */
Type lvalueType = subExpressions[0].getType();
Type rvalueType = subExpressions[1].getType();
subExpressions[0].setType(Type.tSuperType(rvalueType));
subExpressions[1].setType(Type.tSubType(lvalueType));
}
if (!isVoid())
updateParentType(subExpressions[0].getType());
}
public int getOperandCount() {
return 1 + getLValueOperandCount();
public Expression simplify() {
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,
Expression[] operands)
throws java.io.IOException;
public boolean opEquals(Operator o) {
return o instanceof StoreInstruction
&& o.operatorIndex == operatorIndex
&& o.isVoid() == isVoid();
}
public void dumpExpression(TabbedPrintWriter writer, Expression[] operands)
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException
{
dumpLValue(writer, operands);
subExpressions[0].dumpExpression(writer, 950);
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.decompiler.TabbedPrintWriter;
public class StringAddOperator extends SimpleOperator {
public class StringAddOperator extends Operator {
protected Type operandType;
public StringAddOperator() {
super(Type.tString, ADD_OP, 2);
operandTypes[1] = Type.tUnknown;
super(Type.tString, ADD_OP);
initOperands(2);
}
public void clearFirstType() {
operandTypes[0] = Type.tUnknown;
}
public int getPriority() {
return 610;
}
public boolean equals(Object o) {
public boolean opEquals(Operator o) {
return (o instanceof StringAddOperator);
}
public void dumpExpression(TabbedPrintWriter writer,
Expression[] operands)
public void updateSubTypes() {
/* A string add allows everything */
}
public void updateType() {
}
public void dumpExpression(TabbedPrintWriter writer)
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());
operands[1].dumpExpression(writer, 611);
subExpressions[1].dumpExpression(writer, 611);
}
}

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

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

Loading…
Cancel
Save