diff --git a/jode/jode/expr/ArrayLengthOperator.java b/jode/jode/expr/ArrayLengthOperator.java index eca6a55..e4c128f 100644 --- a/jode/jode/expr/ArrayLengthOperator.java +++ b/jode/jode/expr/ArrayLengthOperator.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"); } } diff --git a/jode/jode/expr/ArrayLoadOperator.java b/jode/jode/expr/ArrayLoadOperator.java index 024f425..161ee1b 100644 --- a/jode/jode/expr/ArrayLoadOperator.java +++ b/jode/jode/expr/ArrayLoadOperator.java @@ -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("]"); } } diff --git a/jode/jode/expr/ArrayStoreOperator.java b/jode/jode/expr/ArrayStoreOperator.java index 9d5e5f0..a565889 100644 --- a/jode/jode/expr/ArrayStoreOperator.java +++ b/jode/jode/expr/ArrayStoreOperator.java @@ -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("]"); } } diff --git a/jode/jode/expr/BinaryOperator.java b/jode/jode/expr/BinaryOperator.java index adea755..cbeb926 100644 --- a/jode/jode/expr/BinaryOperator.java +++ b/jode/jode/expr/BinaryOperator.java @@ -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); } } diff --git a/jode/jode/expr/CheckCastOperator.java b/jode/jode/expr/CheckCastOperator.java index 2960448..429e991 100644 --- a/jode/jode/expr/CheckCastOperator.java +++ b/jode/jode/expr/CheckCastOperator.java @@ -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); } } diff --git a/jode/jode/expr/CheckNullOperator.java b/jode/jode/expr/CheckNullOperator.java index 6dfd6ac..758ec5a 100644 --- a/jode/jode/expr/CheckNullOperator.java +++ b/jode/jode/expr/CheckNullOperator.java @@ -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"); } } diff --git a/jode/jode/expr/ClassFieldOperator.java b/jode/jode/expr/ClassFieldOperator.java index 4a9dfa0..30e080c 100644 --- a/jode/jode/expr/ClassFieldOperator.java +++ b/jode/jode/expr/ClassFieldOperator.java @@ -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"); diff --git a/jode/jode/expr/CombineableOperator.java b/jode/jode/expr/CombineableOperator.java index c722d72..cae38b3 100644 --- a/jode/jode/expr/CombineableOperator.java +++ b/jode/jode/expr/CombineableOperator.java @@ -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. diff --git a/jode/jode/expr/CompareBinaryOperator.java b/jode/jode/expr/CompareBinaryOperator.java index 22f644b..e708737 100644 --- a/jode/jode/expr/CompareBinaryOperator.java +++ b/jode/jode/expr/CompareBinaryOperator.java @@ -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); } } diff --git a/jode/jode/expr/CompareToIntOperator.java b/jode/jode/expr/CompareToIntOperator.java index 2f75e95..8c51ceb 100644 --- a/jode/jode/expr/CompareToIntOperator.java +++ b/jode/jode/expr/CompareToIntOperator.java @@ -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); } } diff --git a/jode/jode/expr/CompareUnaryOperator.java b/jode/jode/expr/CompareUnaryOperator.java index 90047b4..11894cf 100644 --- a/jode/jode/expr/CompareUnaryOperator.java +++ b/jode/jode/expr/CompareUnaryOperator.java @@ -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"); } diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java index b994560..aab1725 100644 --- a/jode/jode/expr/ConstOperator.java +++ b/jode/jode/expr/ConstOperator.java @@ -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()); } diff --git a/jode/jode/expr/ConstantArrayOperator.java b/jode/jode/expr/ConstantArrayOperator.java index 1ef7724..8ecab4b 100644 --- a/jode/jode/expr/ConstantArrayOperator.java +++ b/jode/jode/expr/ConstantArrayOperator.java @@ -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); } diff --git a/jode/jode/expr/ConstructorOperator.java b/jode/jode/expr/ConstructorOperator.java index 1b3446a..5240dd6 100644 --- a/jode/jode/expr/ConstructorOperator.java +++ b/jode/jode/expr/ConstructorOperator.java @@ -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(")"); } } - - diff --git a/jode/jode/expr/ConvertOperator.java b/jode/jode/expr/ConvertOperator.java index 0556fdc..3f3a58d 100644 --- a/jode/jode/expr/ConvertOperator.java +++ b/jode/jode/expr/ConvertOperator.java @@ -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); } } diff --git a/jode/jode/expr/Expression.java b/jode/jode/expr/Expression.java index 5fb7464..9312f0a 100644 --- a/jode/jode/expr/Expression.java +++ b/jode/jode/expr/Expression.java @@ -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: + * + */ + 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; } diff --git a/jode/jode/expr/GetFieldOperator.java b/jode/jode/expr/GetFieldOperator.java index df39f75..cc7cd4b 100644 --- a/jode/jode/expr/GetFieldOperator.java +++ b/jode/jode/expr/GetFieldOperator.java @@ -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); } diff --git a/jode/jode/expr/IIncOperator.java b/jode/jode/expr/IIncOperator.java index 8b1b16d..cb6eaa7 100644 --- a/jode/jode/expr/IIncOperator.java +++ b/jode/jode/expr/IIncOperator.java @@ -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); } } + diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java index 3792037..57adc7a 100644 --- a/jode/jode/expr/IfThenElseOperator.java +++ b/jode/jode/expr/IfThenElseOperator.java @@ -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); } } diff --git a/jode/jode/expr/InstanceOfOperator.java b/jode/jode/expr/InstanceOfOperator.java index 0405f38..8d9508d 100644 --- a/jode/jode/expr/InstanceOfOperator.java +++ b/jode/jode/expr/InstanceOfOperator.java @@ -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); } diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java index b306f3a..d9ded9a 100644 --- a/jode/jode/expr/InvokeOperator.java +++ b/jode/jode/expr/InvokeOperator.java @@ -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(")"); + } } diff --git a/jode/jode/expr/LValueExpression.java b/jode/jode/expr/LValueExpression.java new file mode 100644 index 0000000..8c65fa2 --- /dev/null +++ b/jode/jode/expr/LValueExpression.java @@ -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); + } +} diff --git a/jode/jode/expr/LocalLoadOperator.java b/jode/jode/expr/LocalLoadOperator.java index a05e854..9aba19e 100644 --- a/jode/jode/expr/LocalLoadOperator.java +++ b/jode/jode/expr/LocalLoadOperator.java @@ -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()); } } - diff --git a/jode/jode/expr/LocalStoreOperator.java b/jode/jode/expr/LocalStoreOperator.java index 09e307a..a56a926 100644 --- a/jode/jode/expr/LocalStoreOperator.java +++ b/jode/jode/expr/LocalStoreOperator.java @@ -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()); } } diff --git a/jode/jode/expr/LocalVarOperator.java b/jode/jode/expr/LocalVarOperator.java index 988f390..baa8966 100644 --- a/jode/jode/expr/LocalVarOperator.java +++ b/jode/jode/expr/LocalVarOperator.java @@ -31,5 +31,3 @@ public interface LocalVarOperator { */ public void updateType(); } - - diff --git a/jode/jode/expr/MonitorEnterOperator.java b/jode/jode/expr/MonitorEnterOperator.java index 5509048..3c4ab05 100644 --- a/jode/jode/expr/MonitorEnterOperator.java +++ b/jode/jode/expr/MonitorEnterOperator.java @@ -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); } } diff --git a/jode/jode/expr/MonitorExitOperator.java b/jode/jode/expr/MonitorExitOperator.java index 8acd9a0..7ec64f4 100644 --- a/jode/jode/expr/MonitorExitOperator.java +++ b/jode/jode/expr/MonitorExitOperator.java @@ -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); } } diff --git a/jode/jode/expr/NewArrayOperator.java b/jode/jode/expr/NewArrayOperator.java index d66124c..40b5e80 100644 --- a/jode/jode/expr/NewArrayOperator.java +++ b/jode/jode/expr/NewArrayOperator.java @@ -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("]"); } } diff --git a/jode/jode/expr/NewOperator.java b/jode/jode/expr/NewOperator.java index 76e3412..2d0f298 100644 --- a/jode/jode/expr/NewOperator.java +++ b/jode/jode/expr/NewOperator.java @@ -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); } diff --git a/jode/jode/expr/NoArgOperator.java b/jode/jode/expr/NoArgOperator.java index 8dd2a51..ab03f0b 100644 --- a/jode/jode/expr/NoArgOperator.java +++ b/jode/jode/expr/NoArgOperator.java @@ -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() { } } diff --git a/jode/jode/expr/NopOperator.java b/jode/jode/expr/NopOperator.java index 54c600e..25a6e3f 100644 --- a/jode/jode/expr/NopOperator.java +++ b/jode/jode/expr/NopOperator.java @@ -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"); } } diff --git a/jode/jode/expr/Operator.java b/jode/jode/expr/Operator.java index e5f9e9c..1d470e5 100644 --- a/jode/jode/expr/Operator.java +++ b/jode/jode/expr/Operator.java @@ -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: - * + * 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 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); } } diff --git a/jode/jode/expr/StringAddOperator.java b/jode/jode/expr/StringAddOperator.java index a2007c7..4c3c473 100644 --- a/jode/jode/expr/StringAddOperator.java +++ b/jode/jode/expr/StringAddOperator.java @@ -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); } } + diff --git a/jode/jode/expr/ThisOperator.java b/jode/jode/expr/ThisOperator.java index c41625e..d7a43b7 100644 --- a/jode/jode/expr/ThisOperator.java +++ b/jode/jode/expr/ThisOperator.java @@ -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"); } } - diff --git a/jode/jode/expr/UnaryOperator.java b/jode/jode/expr/UnaryOperator.java index ea38967..1722442 100644 --- a/jode/jode/expr/UnaryOperator.java +++ b/jode/jode/expr/UnaryOperator.java @@ -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); } }