*** empty log message ***

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@107 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent a421adad08
commit ba05fcd077
  1. 1483
      jode/jode/bytecode/Opcodes.java
  2. 131
      jode/jode/decompiler/CodeAnalyzer.java
  3. 14
      jode/jode/expr/ComplexExpression.java
  4. 50
      jode/jode/expr/ConstantArrayOperator.java
  5. 5
      jode/jode/expr/ConstructorOperator.java
  6. 3
      jode/jode/expr/Expression.java
  7. 20
      jode/jode/flow/CombineIfGotoExpressions.java
  8. 20
      jode/jode/flow/CompleteSynchronized.java
  9. 7
      jode/jode/flow/ConditionalBlock.java
  10. 61
      jode/jode/flow/CreateAssignExpression.java
  11. 186
      jode/jode/flow/CreateConstantArray.java
  12. 134
      jode/jode/flow/CreateExpression.java
  13. 20
      jode/jode/flow/CreateForInitializer.java
  14. 254
      jode/jode/flow/CreateIfThenElseOperator.java
  15. 69
      jode/jode/flow/CreateNewConstructor.java
  16. 211
      jode/jode/flow/CreatePrePostIncExpression.java
  17. 270
      jode/jode/flow/FlowBlock.java
  18. 10
      jode/jode/flow/InstructionContainer.java
  19. 6
      jode/jode/flow/LoopBlock.java
  20. 69
      jode/jode/flow/RemoveEmpty.java
  21. 5
      jode/jode/flow/SpecialBlock.java
  22. 52
      jode/jode/flow/StructuredBlock.java
  23. 4
      jode/jode/flow/SynchronizedBlock.java
  24. 2
      jode/jode/type/ClassInterfacesType.java

File diff suppressed because it is too large Load Diff

@ -36,7 +36,6 @@ import gnu.bytecode.Spy;
public class CodeAnalyzer implements Analyzer {
TransformExceptionHandlers handler;
FlowBlock methodHeader;
CodeAttr code;
MethodAnalyzer method;
@ -71,19 +70,78 @@ public class CodeAnalyzer implements Analyzer {
param.addElement(getLocalInfo(0, i));
}
void readCode(CodeAttr bincode)
private final static int SEQUENTIAL = 1;
private final static int PREDECESSORS = 2;
/**
* @param code The code array.
* @param handlers The exception handlers.
*/
void readCode(byte[] code, short[] handlers)
throws ClassFormatError
{
byte[] code = bincode.getCode();
byte[] flags = new byte[code.length];
int[] lengths = new int[code.length];
try {
DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code));
for (int addr = 0; addr < code.length; ) {
int[] succs = Opcodes.getSizeAndSuccs(addr, stream);
if (succs.length == 2
&& succs[1] == addr + succs[0])
flags[addr] |= SEQUENTIAL;
lengths[addr] = succs[0];
addr += succs[0];
for (int i=1; i<succs.length; i++)
if (succs[i] != addr)
flags[succs[i]] |= PREDECESSORS;
}
} catch (IOException ex) {
throw new ClassFormatError(ex.toString());
}
for (int i=0; i<handlers.length; i += 4) {
int start = handlers[i + 0];
int handler = handlers[i + 2];
if (start < 0) start += 65536;
if (handler < 0) handler += 65536;
flags[start] |= PREDECESSORS;
flags[handler] |= PREDECESSORS;
}
FlowBlock[] instr = new FlowBlock[code.length];
int returnCount;
try {
DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code));
/* While we read the opcodes into FlowBlocks
* we try to combine sequential blocks, as soon as we
* find two sequential instructions in a row, where the
* second has no predecessors.
*/
int mark = 1000;
FlowBlock lastBlock = null;
for (int addr = 0; addr < code.length; ) {
instr[addr] = Opcodes.readOpcode(addr, stream, this);
jode.flow.StructuredBlock block
= Opcodes.readOpcode(addr, stream, this);
if (jode.Decompiler.isVerbose && addr > mark) {
System.err.print('.');
mark += 1000;
}
addr = instr[addr].getNextAddr();
if (lastBlock != null && flags[addr] == SEQUENTIAL) {
lastBlock.doSequentialT1(block, lengths[addr]);
} else {
instr[addr] = new FlowBlock(this, addr,
lengths[addr], block);
lastBlock = ((flags[addr] & SEQUENTIAL) == 0)
? null : instr[addr];
}
addr += lengths[addr];
}
} catch (IOException ex) {
throw new ClassFormatError(ex.toString());
@ -94,28 +152,74 @@ public class CodeAnalyzer implements Analyzer {
addr = instr[addr].getNextAddr();
}
handler = new TransformExceptionHandlers(instr);
short[] handlers = Spy.getExceptionHandlers(bincode);
TransformExceptionHandlers excHandlers
= new TransformExceptionHandlers(instr);
for (int i=0; i<handlers.length; i += 4) {
Type type = null;
int start = handlers[i + 0];
int end = handlers[i + 1];
int handler = handlers[i + 2];
if (start < 0) start += 65536;
if (end < 0) end += 65536;
if (handler < 0) handler += 65536;
if (handlers[i + 3 ] != 0) {
CpoolClass cpcls = (CpoolClass)
method.classAnalyzer.getConstant(handlers[i + 3]);
type = Type.tClass(cpcls.getName().getString());
}
handler.addHandler(handlers[i + 0], handlers[i + 1],
handlers[i + 2], type);
excHandlers.addHandler(start, end, handler, type);
}
if (Decompiler.isVerbose)
System.err.print('-');
excHandlers.analyze();
methodHeader = instr[0];
methodHeader.analyze();
}
// void readCode(byte[] code, short[] handlers)
// throws ClassFormatError
// {
// FlowBlock[] instr = new FlowBlock[code.length];
// int returnCount;
// try {
// DataInputStream stream =
// new DataInputStream(new ByteArrayInputStream(code));
// for (int addr = 0; addr < code.length; ) {
// instr[addr] = Opcodes.readOpcode(addr, stream, this);
// addr = instr[addr].getNextAddr();
// }
// } catch (IOException ex) {
// throw new ClassFormatError(ex.toString());
// }
// for (int addr=0; addr<instr.length; ) {
// instr[addr].resolveJumps(instr);
// addr = instr[addr].getNextAddr();
// }
// handler = new TransformExceptionHandlers(instr);
// for (int i=0; i<handlers.length; i += 4) {
// Type type = null;
// if (handlers[i + 3 ] != 0) {
// CpoolClass cpcls = (CpoolClass)
// method.classAnalyzer.getConstant(handlers[i + 3]);
// type = Type.tClass(cpcls.getName().getString());
// }
// handler.addHandler(handlers[i + 0], handlers[i + 1],
// handlers[i + 2], type);
// }
// methodHeader = instr[0];
// }
public void analyze()
{
readCode(code);
handler.analyze();
methodHeader.analyze();
byte[] codeArray = code.getCode();
short[] handlers = Spy.getExceptionHandlers(code);
readCode(codeArray, handlers);
Enumeration enum = allLocals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo)enum.nextElement();
@ -157,4 +261,3 @@ public class CodeAnalyzer implements Analyzer {
return method.classAnalyzer.clazz;
}
}

@ -132,6 +132,10 @@ public class ComplexExpression extends Expression {
return subExpressions;
}
public void setSubExpressions(int i, Expression expr) {
subExpressions[i] = expr;
updateSubTypes();
}
void updateSubTypes() {
for (int i=0; i < subExpressions.length; i++) {
if (i == 0 && operator instanceof ArrayStoreOperator) {
@ -255,7 +259,7 @@ public class ComplexExpression extends Expression {
return new ComplexExpression
(new StringAddOperator(), new Expression[]
{ e, subExpressions[1] });
{ e, subExpressions[1].simplifyString() });
}
if (operator instanceof ConstructorOperator
&& (((ConstructorOperator) operator).getClassType()
@ -263,7 +267,7 @@ public class ComplexExpression extends Expression {
if (subExpressions.length == 1 &&
subExpressions[0].getType().isOfType(Type.tString))
return subExpressions[0];
return subExpressions[0].simplifyString();
}
return null;
}
@ -278,6 +282,7 @@ public class ComplexExpression extends Expression {
Expression simple = subExpressions[0].simplifyStringBuffer();
if (simple != null)
return simple;
}
else if (invoke.getMethodName().equals("valueOf")
&& invoke.isStatic()
@ -309,7 +314,7 @@ public class ComplexExpression extends Expression {
{ left, right });
}
}
return null;
return this;
}
public Expression simplify() {
@ -382,7 +387,7 @@ public class ComplexExpression extends Expression {
}
else {
Expression stringExpr = simplifyString();
if (stringExpr != null)
if (stringExpr != this)
return stringExpr.simplify();
}
for (int i=0; i< subExpressions.length; i++) {
@ -392,3 +397,4 @@ public class ComplexExpression extends Expression {
return this;
}
}

@ -19,30 +19,56 @@
package jode;
public class ConstantArrayOperator extends SimpleOperator {
public class ConstantArrayOperator extends NoArgOperator {
ConstOperator empty;
Expression[] values;
Type argType;
public ConstantArrayOperator(Type type, int size) {
super(type, 0, size);
for (int i=0; i< size; i++)
operandTypes[i] = ((ArrayType)type).getElementType();
super(type);
values = new Expression[size];
argType = (type instanceof ArrayType)
? Type.tSubType(((ArrayType)type).getElementType()) : Type.tError;
empty = new ConstOperator(argType, "0");
}
public int getPriority() {
return 200;
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 int getOperandPriority(int i) {
return 0;
public boolean setValue(int index, Expression value) {
if (index < 0 || index > values.length || values[index] != null)
return false;
value.setType(argType);
setType(Type.tSuperType(Type.tArray(value.getType())));
values[index] = value;
value.parent = this;
return true;
}
public int getPriority() {
return 200;
}
public String toString(String[] operands) {
StringBuffer result
= new StringBuffer("new "+type+" {");
for (int i=0; i< getOperandCount(); i++) {
= new StringBuffer("new ").append(type).append(" { ");
for (int i=0; i< values.length; i++) {
if (i>0)
result.append(", ");
result.append(operands[i]);
result.append((values[i] != null) ? values[i] : empty);
}
return result.append("}").toString();
return result.append(" }").toString();
}
}

@ -24,8 +24,9 @@ public class ConstructorOperator extends Operator {
MethodType methodType;
Type classType;
public ConstructorOperator(Type type, MethodType methodType) {
super(type, 0);
public ConstructorOperator(Type type, MethodType methodType,
boolean isVoid) {
super(isVoid ? Type.tVoid : type, 0);
this.classType = type;
this.methodType = methodType;
}

@ -107,6 +107,9 @@ public abstract class Expression {
public Expression simplify() {
return this;
}
public Expression simplifyString() {
return this;
}
static Expression EMPTYSTRING = new ConstOperator(Type.tString, "\"\"");

@ -24,14 +24,14 @@ import jode.ComplexExpression;
import jode.Type;
import jode.BinaryOperator;
public class CombineIfGotoExpressions implements Transformation{
public class CombineIfGotoExpressions {
public boolean transform(FlowBlock flow) {
if (!(flow.lastModified instanceof ConditionalBlock)
|| !(flow.lastModified.outer instanceof SequentialBlock))
public static boolean transform(ConditionalBlock cb,
StructuredBlock last) {
if (cb.jump == null
|| !(last.outer instanceof SequentialBlock))
return false;
ConditionalBlock cb = (ConditionalBlock) flow.lastModified;
SequentialBlock sequBlock = (SequentialBlock) cb.outer;
Expression[] e = new Expression[2];
@ -77,6 +77,10 @@ public class CombineIfGotoExpressions implements Transformation{
} else
return false;
/* We have changed some instructions above. We may never
* return with a failure now.
*/
sequBlock = (SequentialBlock) cb.outer;
while (sequBlock.subBlocks[0] instanceof InstructionBlock) {
/* Now combine the expression. Everything should
@ -90,14 +94,14 @@ public class CombineIfGotoExpressions implements Transformation{
sequBlock = (SequentialBlock) sequBlock.outer;
}
flow.removeSuccessor(prevJump);
cb.flowBlock.removeSuccessor(prevJump);
prevJump.prev.removeJump();
Expression cond =
new ComplexExpression
(new BinaryOperator(Type.tBoolean, operator), e);
cb.setInstruction(cond);
cb.moveDefinitions(sequBlock, cb);
cb.replace(sequBlock);
cb.moveDefinitions(sequBlock, last);
last.replace(sequBlock);
return true;
}
return false;

@ -24,22 +24,20 @@ import jode.LocalLoadOperator;
import jode.LocalStoreOperator;
import jode.Expression;
public class CompleteSynchronized implements Transformation {
public class CompleteSynchronized {
/**
* This combines the monitorenter and the initial expression
* into a synchronized statement
* @param flow The FlowBlock that is transformed
*/
public boolean transform(FlowBlock flow) {
public static boolean transform(SynchronizedBlock synBlock,
StructuredBlock last) {
if (!(flow.lastModified instanceof SynchronizedBlock)
|| flow.lastModified.outer == null)
if (!(last.outer instanceof SequentialBlock))
return false;
/* If the program is well formed, the following succeed */
SynchronizedBlock synBlock = (SynchronizedBlock) flow.lastModified;
try {
SequentialBlock sequBlock = (SequentialBlock) synBlock.outer;
@ -59,11 +57,11 @@ public class CompleteSynchronized implements Transformation {
System.err.print('s');
synBlock.isEntered = true;
synBlock.moveDefinitions(synBlock.outer,synBlock);
synBlock.replace(synBlock.outer);
synBlock.moveDefinitions(last.outer,last);
last.replace(last.outer);
/* Is there another expression? */
if (synBlock.outer == null)
if (!(last.outer instanceof SynchronizedBlock))
return false;
Expression object;
@ -85,8 +83,8 @@ public class CompleteSynchronized implements Transformation {
}
synBlock.object = object;
synBlock.moveDefinitions(synBlock.outer,synBlock);
synBlock.replace(synBlock.outer);
synBlock.moveDefinitions(last.outer,last);
last.replace(last.outer);
return true;
}
}

@ -76,4 +76,11 @@ public class ConditionalBlock extends InstructionContainer {
trueBlock.dumpSource(writer);
writer.untab();
}
public boolean doTransformations() {
StructuredBlock last = flowBlock.lastModified;
return super.doTransformations()
|| CombineIfGotoExpressions.transform(this, last)
|| CreateIfThenElseOperator.createFunny(this, last);
}
}

@ -20,21 +20,20 @@
package jode.flow;
import jode.*;
public class CreateAssignExpression implements Transformation{
public boolean transform(FlowBlock flow) {
if (!(flow.lastModified instanceof InstructionContainer)
|| !(flow.lastModified.outer instanceof SequentialBlock)
|| !(((InstructionContainer)flow.lastModified).getInstruction()
instanceof StoreInstruction)
|| !(((InstructionContainer)flow.lastModified).getInstruction()
.isVoid()))
public class CreateAssignExpression {
public static boolean transform(InstructionContainer ic,
StructuredBlock last) {
if (!(last.outer instanceof SequentialBlock)
|| !(ic.getInstruction() instanceof StoreInstruction)
|| !(ic.getInstruction().isVoid()))
return false;
return (createAssignOp(flow) || createAssignExpression(flow));
return (createAssignOp(ic, last) || createAssignExpression(ic, last));
}
public boolean createAssignOp(FlowBlock flow) {
public static boolean createAssignOp(InstructionContainer ic,
StructuredBlock last) {
/* Situation:
*
@ -53,11 +52,8 @@ public class CreateAssignExpression implements Transformation{
*
* If the optional dup is present the store*= becomes non void.
*/
InstructionContainer lastBlock
= (InstructionContainer) flow.lastModified;
SequentialBlock opBlock = (SequentialBlock) lastBlock.outer;
StoreInstruction store
= (StoreInstruction) lastBlock.getInstruction();
SequentialBlock opBlock = (SequentialBlock) last.outer;
StoreInstruction store = (StoreInstruction) ic.getInstruction();
boolean isAssignOp = false;
if (opBlock.subBlocks[0] instanceof SpecialBlock) {
@ -94,6 +90,12 @@ public class CreateAssignExpression implements Transformation{
int opIndex;
Expression rightHandSide;
if (expr.getOperator() instanceof ConvertOperator
&& expr.getSubExpressions()[0] instanceof ComplexExpression
&& expr.getOperator().getType().isOfType(store.getLValueType())) {
expr = (ComplexExpression) expr.getSubExpressions()[0];
}
if (expr.getOperator() instanceof BinaryOperator) {
BinaryOperator binop = (BinaryOperator) expr.getOperator();
@ -109,24 +111,24 @@ public class CreateAssignExpression implements Transformation{
Expression simple = expr.simplifyString();
rightHandSide = simple;
/* Now search for the leftmost operand ... */
ComplexExpression last = null;
ComplexExpression lastExpr = null;
while (simple instanceof ComplexExpression
&& simple.getOperator() instanceof StringAddOperator) {
last = (ComplexExpression) simple;
simple = last.getSubExpressions()[0];
lastExpr = (ComplexExpression) simple;
simple = lastExpr.getSubExpressions()[0];
}
/* ... check it ... */
if (last == null || !(simple instanceof Operator)
if (lastExpr == null || !(simple instanceof Operator)
|| !store.matches((Operator) simple))
return false;
/* ... and remove it. */
if (last.getParent() != null) {
((ComplexExpression)last.getParent()).getSubExpressions()[0]
= last.getSubExpressions()[1];
if (lastExpr.getParent() != null) {
((ComplexExpression)lastExpr.getParent())
.setSubExpressions(0,lastExpr.getSubExpressions()[1]);
} else
rightHandSide = last.getSubExpressions()[1];
rightHandSide = lastExpr.getSubExpressions()[1];
opIndex = Operator.ADD_OP;
}
@ -137,20 +139,19 @@ public class CreateAssignExpression implements Transformation{
if (isAssignOp)
store.makeNonVoid();
lastBlock.replace(opBlock.subBlocks[1]);
last.replace(opBlock.subBlocks[1]);
return true;
}
public boolean createAssignExpression(FlowBlock flow) {
public static boolean createAssignExpression(InstructionContainer ic,
StructuredBlock last) {
/* Situation:
* sequBlock:
* dup_X(lvalue_count)
* store instruction
*/
InstructionContainer lastBlock
= (InstructionContainer) flow.lastModified;
SequentialBlock sequBlock = (SequentialBlock) lastBlock.outer;
StoreInstruction store = (StoreInstruction) lastBlock.getInstruction();
SequentialBlock sequBlock = (SequentialBlock) last.outer;
StoreInstruction store = (StoreInstruction) ic.getInstruction();
if (sequBlock.subBlocks[0] instanceof SpecialBlock) {

@ -26,104 +26,106 @@ import jode.ConstantArrayOperator;
import jode.ConstOperator;
import jode.Type;
public class CreateConstantArray implements Transformation {
public boolean transform(FlowBlock flow) {
InstructionBlock lastBlock;
SequentialBlock sequBlock;
Expression[] consts = null;
int count = 0;
Type type;
try {
InstructionBlock ib = (InstructionBlock) flow.lastModified;
sequBlock = (SequentialBlock) ib.outer;
ib = (InstructionBlock) sequBlock.subBlocks[0];
lastBlock = ib;
int lastindex = -1;
while (ib.getInstruction() instanceof ArrayStoreOperator) {
ArrayStoreOperator store =
(ArrayStoreOperator) ib.getInstruction();
sequBlock = (SequentialBlock) sequBlock.outer;
ib = (InstructionBlock) sequBlock.subBlocks[0];
Expression lastconst = ib.getInstruction();
sequBlock = (SequentialBlock) sequBlock.outer;
ib = (InstructionBlock) sequBlock.subBlocks[0];
Expression indexexpr = ib.getInstruction();
ConstOperator indexop =
(ConstOperator) indexexpr.getOperator();
if (!indexop.getType().isOfType(Type.tUInt))
return false;
int index = Integer.parseInt(indexop.getValue());
if (index >= 0 && consts == null) {
lastindex = index;
consts = new Expression[lastindex+1];
} else if (index < 0 || index > lastindex)
return false;
else {
while (index < lastindex) {
consts[lastindex--] =
new ConstOperator(Type.tUnknown, "0");
}
}
consts[lastindex--] = lastconst;
sequBlock = (SequentialBlock) sequBlock.outer;
public class CreateConstantArray {
SpecialBlock dup = (SpecialBlock) sequBlock.subBlocks[0];
if (dup.type != SpecialBlock.DUP
|| dup.depth != 0
|| dup.count != store.getLValueType().stackSize())
return false;
count++;
sequBlock = (SequentialBlock) sequBlock.outer;
ib = (InstructionBlock) sequBlock.subBlocks[0];
}
if (count == 0)
public static boolean transform(InstructionContainer ic,
StructuredBlock last) {
/* Situation:
* PUSH new Array[]
* DUP
* PUSH index
* PUSH value
* stack_2[stack_1] = stack_0
* ...
*/
if (last.outer instanceof SequentialBlock) {
SequentialBlock sequBlock = (SequentialBlock) last.outer;
if (!(ic.getInstruction() instanceof ArrayStoreOperator)
|| !(sequBlock.subBlocks[0] instanceof InstructionBlock)
|| !(sequBlock.outer instanceof SequentialBlock))
return false;
ArrayStoreOperator store
= (ArrayStoreOperator) ic.getInstruction();
InstructionBlock ib = (InstructionBlock)sequBlock.subBlocks[0];
sequBlock = (SequentialBlock) sequBlock.outer;
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock)
|| !(sequBlock.outer instanceof SequentialBlock))
return false;
while (lastindex >= 0)
consts[lastindex--] = new ConstOperator(Type.tUnknown, "0");
ComplexExpression newArrayExpr =
(ComplexExpression) ib.getInstruction();
NewArrayOperator newArrayOp =
(NewArrayOperator) newArrayExpr.getOperator();
type = newArrayOp.getType();
if (newArrayOp.getOperandCount() != 1)
Expression expr = ib.getInstruction();
ib = (InstructionBlock)sequBlock.subBlocks[0];
sequBlock = (SequentialBlock) sequBlock.outer;
if (expr.getOperandCount() > 0
|| !(ib.getInstruction() instanceof ConstOperator)
|| !(sequBlock.subBlocks[0] instanceof SpecialBlock)
|| !(sequBlock.outer instanceof SequentialBlock))
return false;
Expression countexpr = newArrayExpr.getSubExpressions()[0];
ConstOperator countop =
(ConstOperator) countexpr.getOperator();
if (!countop.getType().isOfType(Type.tUInt))
ConstOperator indexOp = (ConstOperator) ib.getInstruction();
SpecialBlock dup = (SpecialBlock) sequBlock.subBlocks[0];
sequBlock = (SequentialBlock) sequBlock.outer;
if (!indexOp.getType().isOfType(Type.tUInt)
|| dup.type != SpecialBlock.DUP
|| dup.depth != 0
|| dup.count != store.getLValueType().stackSize()
|| !(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
int arraylength = Integer.parseInt(countop.getValue());
if (arraylength != consts.length) {
if (arraylength < consts.length)
int index = Integer.parseInt(indexOp.getValue());
ib = (InstructionBlock)sequBlock.subBlocks[0];
if (ib.getInstruction() instanceof ComplexExpression
&& (ib.getInstruction().getOperator()
instanceof NewArrayOperator)) {
/* This is the first element */
ComplexExpression newArrayExpr =
(ComplexExpression) ib.getInstruction();
NewArrayOperator newArrayOp =
(NewArrayOperator) newArrayExpr.getOperator();
if (newArrayOp.getOperandCount() != 1
|| !(newArrayExpr.getSubExpressions()[0]
instanceof ConstOperator))
return false;
ConstOperator countop =
(ConstOperator) newArrayExpr.getSubExpressions()[0];
if (!countop.getType().isOfType(Type.tUInt))
return false;
int arraylength = Integer.parseInt(countop.getValue());
if (arraylength <= index)
return false;
Expression[] newConsts = new Expression[arraylength];
System.arraycopy(consts, 0, newConsts, 0, consts.length);
for (int i=consts.length; i<arraylength; i++)
newConsts[i] = new ConstOperator(Type.tUnknown, "0");
consts = newConsts;
if (jode.Decompiler.isVerbose)
System.err.print('a');
ConstantArrayOperator cao
= new ConstantArrayOperator(newArrayOp.getType(),
arraylength);
cao.setValue(index, expr);
ic.setInstruction(cao);
ic.moveDefinitions(sequBlock, last);
last.replace(sequBlock);
return true;
} else if (ib.getInstruction() instanceof ConstantArrayOperator) {
ConstantArrayOperator cao
= (ConstantArrayOperator) ib.getInstruction();
if (cao.setValue(index, expr)) {
/* adding Element succeeded */
ic.setInstruction(cao);
ic.moveDefinitions(sequBlock, last);
last.replace(sequBlock);
return true;
}
}
} catch (NullPointerException ex) {
return false;
} catch (ClassCastException ex) {
return false;
}
if (jode.Decompiler.isVerbose)
System.err.print("a");
lastBlock.setInstruction
(new ComplexExpression
(new ConstantArrayOperator(type, consts.length),
consts));
lastBlock.moveDefinitions(sequBlock.subBlocks[0], lastBlock);
lastBlock.replace(sequBlock.subBlocks[0]);
flow.lastModified.moveDefinitions(sequBlock.subBlocks[1],
flow.lastModified);
flow.lastModified.replace(sequBlock.subBlocks[1]);
return true;
return false;
}
}

@ -19,6 +19,7 @@
package jode.flow;
import jode.Operator;
import jode.NopOperator;
import jode.Expression;
import jode.ComplexExpression;
@ -32,88 +33,103 @@ import jode.ComplexExpression;
* expr(op, [ expr_1, ..., expr_n ])
* </pre>
*/
public class CreateExpression implements Transformation {
public class CreateExpression {
/**
* This does the transformation.
* @param FlowBlock the flow block to transform.
* @return true if flow block was simplified.
*/
public boolean transform(FlowBlock flow) {
if (!(flow.lastModified instanceof InstructionContainer)
|| !(flow.lastModified.outer instanceof SequentialBlock))
return false;
public static boolean transform(InstructionContainer ic,
StructuredBlock last) {
InstructionContainer ic = (InstructionContainer) flow.lastModified;
if (ic.getInstruction() instanceof Operator) {
int params = ic.getInstruction().getOperandCount();
if (params == 0)
return false;
Operator op = (Operator) ic.getInstruction();
int params = op.getOperandCount();
if (params == 0)
return false;
ComplexExpression parent = null;
Expression inner = ic.getInstruction();
while (inner instanceof ComplexExpression) {
parent = (ComplexExpression)inner;
inner = parent.getSubExpressions()[0];
}
Expression[] exprs = new Expression[params];
if (!(inner instanceof Operator))
return false;
SequentialBlock sequBlock = (SequentialBlock) ic.outer;
Operator op = (Operator)inner;
Expression lastExpression = null;
/* First check if Expression can be created, but do nothing yet.
*/
for (int i=params; ;) {
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
if (!(last.outer instanceof SequentialBlock))
return false;
SequentialBlock sequBlock = (SequentialBlock)last.outer;
InstructionBlock block =
(InstructionBlock) sequBlock.subBlocks[0];
/* First check if Expression can be created, but do nothing yet.
*/
Expression lastExpression = null;
for (int i = params;;) {
Expression expr = block.getInstruction();
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
if (!expr.isVoid()) {
if (--i == 0)
break;
} else if (lastExpression != null
&& lastExpression.canCombine(expr) <= 0)
return false;
Expression expr =
((InstructionBlock) sequBlock.subBlocks[0]).getInstruction();
if (expr.getOperandCount() > 0)
/* This is a not fully resolved expression in the
* middle, we must not touch it. */
return false;
lastExpression = expr;
if (!(sequBlock.outer instanceof SequentialBlock))
return false;
sequBlock = (SequentialBlock)sequBlock.outer;
}
if (!expr.isVoid()) {
if (--i == 0)
break;
} else if (lastExpression == null
|| lastExpression.canCombine(expr) <= 0)
return false;
/* Now, do the combination. Everything must succeed now.
*/
sequBlock = (SequentialBlock) ic.outer;
for (int i=params; ;) {
if (expr.getOperandCount() > 0)
/* This is a not fully resolved expression in the
* middle, we must not touch it. */
return false;
InstructionBlock block =
(InstructionBlock) sequBlock.subBlocks[0];
lastExpression = expr;
Expression expr = block.getInstruction();
if (!(sequBlock.outer instanceof SequentialBlock))
return false;
sequBlock = (SequentialBlock) sequBlock.outer;
}
if (!expr.isVoid()) {
exprs[--i] = expr;
if (i == 0)
break;
} else
exprs[i] = exprs[i].combine(expr);
/* Now, do the combination. Everything must succeed now.
*/
Expression[] exprs = new Expression[params];
sequBlock = (SequentialBlock) last.outer;
for (int i=params; ;) {
sequBlock = (SequentialBlock)sequBlock.outer;
}
Expression expr =
((InstructionBlock) sequBlock.subBlocks[0]).getInstruction();
if(jode.Decompiler.isVerbose)
System.err.print('x');
if (!expr.isVoid()) {
exprs[--i] = expr;
if (i == 0)
break;
} else
exprs[i] = exprs[i].combine(expr);
ic.setInstruction(new ComplexExpression(op, exprs));
ic.moveDefinitions(sequBlock, ic);
ic.replace(sequBlock);
return true;
sequBlock = (SequentialBlock)sequBlock.outer;
}
return false;
if(jode.Decompiler.isVerbose)
System.err.print('x');
Expression newExpr;
if (params == 1 && op instanceof NopOperator) {
exprs[0].setType(op.getType());
newExpr = exprs[0];
} else
newExpr = new ComplexExpression(op, exprs);
if (parent != null)
parent.setSubExpressions(0, newExpr);
else
ic.setInstruction(newExpr);
ic.moveDefinitions(sequBlock, last);
last.replace(sequBlock);
return true;
}
}

@ -21,23 +21,19 @@ package jode.flow;
import jode.Expression;
import jode.StoreInstruction;
public class CreateForInitializer implements Transformation {
public class CreateForInitializer {
/**
* This combines an variable initializer into a for statement
* @param flow The FlowBlock that is transformed
* @param forBlock the for block
* @param last the lastModified of the flow block.
*/
public boolean transform(FlowBlock flow) {
public static boolean transform(LoopBlock forBlock, StructuredBlock last) {
if (!(flow.lastModified instanceof LoopBlock)
|| !(flow.lastModified.outer instanceof SequentialBlock))
if (!(last.outer instanceof SequentialBlock))
return false;
LoopBlock forBlock = (LoopBlock) flow.lastModified;
if (forBlock.type != forBlock.FOR || forBlock.init != null)
return false;
SequentialBlock sequBlock = (SequentialBlock) forBlock.outer;
SequentialBlock sequBlock = (SequentialBlock) last.outer;
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
@ -53,8 +49,8 @@ public class CreateForInitializer implements Transformation {
System.err.print('f');
forBlock.init = initializer;
forBlock.moveDefinitions(forBlock.outer, forBlock);
forBlock.replace(forBlock.outer);
forBlock.moveDefinitions(last.outer, null);
last.replace(last.outer);
return true;
}
}

@ -20,24 +20,109 @@
package jode.flow;
import jode.Type;
import jode.Expression;
import jode.ConstOperator;
import jode.ComplexExpression;
import jode.IfThenElseOperator;
import jode.CompareUnaryOperator;
import java.util.Enumeration;
import java.util.Vector;
public class CreateIfThenElseOperator implements Transformation {
public class CreateIfThenElseOperator {
/**
* This handles the body of createFunny. There are three cases:
*
* <pre>
* --------
* IF (c2)
* GOTO trueDest -> PUSH c2
* PUSH false
* --------
* PUSH bool -> PUSH bool
* --------
* if (c2)
* (handled recursively) -> PUSH (c2 ? expr1 : expr2)
* else
* (handled recursively)
* --------
* </pre>
*/
private static boolean createFunnyHelper(FlowBlock trueDest,
FlowBlock falseDest,
StructuredBlock block) {
if (block instanceof InstructionBlock
&& !((InstructionBlock)block).getInstruction().isVoid())
return true;
if (block instanceof IfThenElseBlock) {
IfThenElseBlock ifBlock = (IfThenElseBlock) block;
Expression expr1, expr2;
if (ifBlock.elseBlock == null)
return false;
if (!createFunnyHelper(trueDest, falseDest, ifBlock.thenBlock)
| !createFunnyHelper(trueDest, falseDest, ifBlock.elseBlock))
return false;
if (jode.Decompiler.isVerbose)
System.err.print('?');
IfThenElseOperator iteo = new IfThenElseOperator(Type.tBoolean);
((InstructionBlock)ifBlock.thenBlock).setInstruction
(new ComplexExpression
(iteo, new Expression[] {
ifBlock.cond,
((InstructionBlock) ifBlock.thenBlock).getInstruction(),
((InstructionBlock) ifBlock.elseBlock).getInstruction()
}));
ifBlock.thenBlock.moveDefinitions(ifBlock, null);
ifBlock.thenBlock.replace(ifBlock);
return true;
}
if (block instanceof SequentialBlock
&& block.getSubBlocks()[0] instanceof ConditionalBlock
&& block.getSubBlocks()[1] instanceof InstructionBlock) {
ConditionalBlock condBlock =
(ConditionalBlock) block.getSubBlocks()[0];
InstructionBlock pushBlock =
(InstructionBlock) block.getSubBlocks()[1];
if (!(pushBlock.getInstruction() instanceof ConstOperator))
return false;
ConstOperator constOp =
(ConstOperator) pushBlock.getInstruction();
if (condBlock.trueBlock.jump.destination == trueDest
&& constOp.getValue().equals("0")) {
Expression cond = condBlock.getInstruction();
condBlock.flowBlock.removeSuccessor(condBlock.trueBlock.jump);
condBlock.trueBlock.removeJump();
pushBlock.setInstruction(cond);
pushBlock.moveDefinitions(block, null);
pushBlock.replace(block);
return true;
}
}
return false;
}
/**
* This handles the more complicated form of the ?-:-operator used
* in a conditional block. The simplest case is:
* <pre>
* if (cond)
* push e1
* PUSH e1
* else {
* IF (c2)
* GOTO flow_2_
* push 0
* PUSH false
* }
* -&gt;IF (stack_0 == 0)
* GOTO flow_1_
@ -60,104 +145,35 @@ public class CreateIfThenElseOperator implements Transformation {
* sometimes be better replaced with a correct jump.
* @param flow The FlowBlock that is transformed
*/
public boolean createFunny(FlowBlock flow) {
if (!(flow.lastModified instanceof ConditionalBlock))
return false;
ConditionalBlock conditional = (ConditionalBlock) flow.lastModified;
public static boolean createFunny(ConditionalBlock cb,
StructuredBlock last) {
if (!(conditional.trueBlock instanceof EmptyBlock)
|| conditional.trueBlock.jump == null
|| conditional.jump == null
|| !(conditional.getInstruction() instanceof CompareUnaryOperator))
if (cb.jump == null
|| !(cb.getInstruction() instanceof CompareUnaryOperator)
|| !(last.outer instanceof SequentialBlock)
|| !(last.outer.getSubBlocks()[0] instanceof IfThenElseBlock))
return false;
CompareUnaryOperator compare =
(CompareUnaryOperator) conditional.getInstruction();
(CompareUnaryOperator) cb.getInstruction();
FlowBlock trueDestination;
if (compare.getOperatorIndex() == compare.EQUALS_OP)
trueDestination = conditional.jump.destination;
else if (compare.getOperatorIndex() == compare.NOTEQUALS_OP)
trueDestination = conditional.trueBlock.jump.destination;
else
FlowBlock falseDestination;
if (compare.getOperatorIndex() == compare.EQUALS_OP) {
trueDestination = cb.jump.destination;
falseDestination = cb.trueBlock.jump.destination;
} else if (compare.getOperatorIndex() == compare.NOTEQUALS_OP) {
falseDestination = cb.jump.destination;
trueDestination = cb.trueBlock.jump.destination;
} else
return false;
Expression[] e = new Expression[3];
IfThenElseBlock ifBlock;
try {
SequentialBlock sequBlock =
(SequentialBlock) conditional.outer;
ifBlock = (IfThenElseBlock) sequBlock.subBlocks[0];
while (true) {
if (ifBlock.thenBlock instanceof IfThenElseBlock)
ifBlock = (IfThenElseBlock) ifBlock.thenBlock;
else if (ifBlock.elseBlock instanceof IfThenElseBlock)
ifBlock = (IfThenElseBlock) ifBlock.elseBlock;
else
break;
}
e[0] = ifBlock.cond;
StructuredBlock[] subBlocks = ifBlock.getSubBlocks();
if (subBlocks.length != 2)
return false;
for (int i=0; i< 2; i++) {
if (subBlocks[i] instanceof InstructionBlock) {
e[i+1] = ((InstructionBlock)subBlocks[i]).getInstruction();
continue;
}
sequBlock = (SequentialBlock) subBlocks[i];
ConditionalBlock condBlock =
(ConditionalBlock) sequBlock.subBlocks[0];
InstructionBlock pushBlock =
(InstructionBlock) sequBlock.subBlocks[1];
jode.ConstOperator zero =
(jode.ConstOperator) pushBlock.getInstruction();
if (!zero.getValue().equals("0"))
return false;
if (!(condBlock.trueBlock instanceof EmptyBlock)
|| condBlock.trueBlock.jump == null
|| condBlock.jump != null
|| condBlock.trueBlock.jump.destination
!= trueDestination)
return false;
Expression cond = condBlock.getInstruction();
flow.removeSuccessor(condBlock.trueBlock.jump);
condBlock.trueBlock.removeJump();
pushBlock.setInstruction(cond);
pushBlock.moveDefinitions(sequBlock, pushBlock);
pushBlock.replace(sequBlock);
e[i+1] = cond;
}
} catch (ClassCastException ex) {
return false;
} catch (NullPointerException ex) {
return false;
}
if (jode.Decompiler.isVerbose)
System.err.print('?');
IfThenElseOperator iteo = new IfThenElseOperator
(Type.tSuperType(e[1].getType())
.intersection(Type.tSuperType(e[2].getType())));
((InstructionBlock)ifBlock.thenBlock).
setInstruction(new ComplexExpression(iteo, e));
ifBlock.thenBlock.moveDefinitions(ifBlock, ifBlock.thenBlock);
ifBlock.thenBlock.replace(ifBlock);
return true;
SequentialBlock sequBlock = (SequentialBlock) last.outer;
return createFunnyHelper(trueDestination, falseDestination,
sequBlock.subBlocks[0]);
}
/**
@ -176,57 +192,47 @@ public class CreateIfThenElseOperator implements Transformation {
* The <code>-&gt;</code> points to the lastModified block.
* @param flow The FlowBlock that is transformed
*/
public boolean create(FlowBlock flow) {
public static boolean create(InstructionContainer ic,
StructuredBlock last) {
Expression e[] = new Expression[3];
InstructionBlock thenBlock;
try {
InstructionBlock elseBlock = (InstructionBlock) flow.lastModified;
SequentialBlock sequBlock = (SequentialBlock)elseBlock.outer;
if (sequBlock.subBlocks[1] != elseBlock)
return false;
IfThenElseBlock ifBlock = (IfThenElseBlock)sequBlock.subBlocks[0];
if (ifBlock.elseBlock != null)
return false;
thenBlock = (InstructionBlock) ifBlock.thenBlock;
if (ic.jump == null
|| !(last.outer instanceof SequentialBlock))
return false;
SequentialBlock sequBlock = (SequentialBlock)last.outer;
if (!(sequBlock.subBlocks[0] instanceof IfThenElseBlock))
return false;
if (thenBlock.jump.destination != elseBlock.jump.destination)
return false;
IfThenElseBlock ifBlock = (IfThenElseBlock)sequBlock.subBlocks[0];
if (!(ifBlock.thenBlock instanceof InstructionBlock)
|| ifBlock.thenBlock.jump == null
|| ifBlock.thenBlock.jump.destination != ic.jump.destination
|| ifBlock.elseBlock != null)
return false;
e[1] = thenBlock.getInstruction();
if (e[1].isVoid())
return false;
e[2] = elseBlock.getInstruction();
if (e[2].isVoid())
return false;
e[0] = ifBlock.cond;
thenBlock = (InstructionBlock) ifBlock.thenBlock;
} catch (ClassCastException ex) {
e[1] = thenBlock.getInstruction();
if (e[1].isVoid())
return false;
} catch (NullPointerException ex) {
e[2] = ic.getInstruction();
if (e[2].isVoid())
return false;
}
e[0] = ifBlock.cond;
if (jode.Decompiler.isVerbose)
System.err.print('?');
flow.removeSuccessor(thenBlock.jump);
thenBlock.flowBlock.removeSuccessor(thenBlock.jump);
thenBlock.removeJump();
IfThenElseOperator iteo = new IfThenElseOperator
(Type.tSuperType(e[1].getType())
.intersection(Type.tSuperType(e[2].getType())));
((InstructionBlock)flow.lastModified).
setInstruction(new ComplexExpression(iteo, e));
flow.lastModified.moveDefinitions(flow.lastModified.outer, null);
flow.lastModified.replace(flow.lastModified.outer);
ic.setInstruction(new ComplexExpression(iteo, e));
ic.moveDefinitions(last.outer, last);
last.replace(last.outer);
return true;
}
public boolean transform(FlowBlock flow) {
return createFunny(flow) || create(flow);
}
}

@ -24,13 +24,14 @@ import jode.ComplexExpression;
import jode.ConstructorOperator;
import jode.NewOperator;
public class CreateNewConstructor implements Transformation{
public class CreateNewConstructor {
public boolean transform(FlowBlock flow) {
public static boolean transform(InstructionContainer ic,
StructuredBlock last) {
/* Situation:
*
* new <object>
* DUP
* (optional DUP)
* PUSH load_ops
* optionally: <= used for "string1 += string2"
* DUP_X2
@ -43,23 +44,21 @@ public class CreateNewConstructor implements Transformation{
* optionally:
* DUP_X1 <= remove the depth
* [ n non void + some void expressions ]
* PUSH new <object>(stack_n-1,...,stack_0)
* (optional PUSH) new <object>(stack_n-1,...,stack_0)
*/
if (!(flow.lastModified instanceof InstructionBlock)
|| !(flow.lastModified.outer instanceof SequentialBlock))
if (!(last.outer instanceof SequentialBlock))
return false;
InstructionBlock block = (InstructionBlock) flow.lastModified;
if (!(block.getInstruction() instanceof InvokeOperator))
if (!(ic.getInstruction() instanceof InvokeOperator))
return false;
InvokeOperator constrCall = (InvokeOperator) block.getInstruction();
InvokeOperator constrCall = (InvokeOperator) ic.getInstruction();
if (!constrCall.isConstructor())
return false;
/* The rest should probably succeed */
int params = constrCall.getOperandCount() - 1;
SpecialBlock optDup = null;
SequentialBlock sequBlock = (SequentialBlock) block.outer;
SpecialBlock optDupX2 = null;
SequentialBlock sequBlock = (SequentialBlock) last.outer;
while (params > 0) {
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
@ -74,11 +73,11 @@ public class CreateNewConstructor implements Transformation{
instanceof SpecialBlock) {
/* handle the optional dup */
sequBlock = (SequentialBlock) sequBlock.outer;
optDup = (SpecialBlock) sequBlock.subBlocks[0];
if (optDup.type != SpecialBlock.DUP
|| optDup.depth != 2)
optDupX2 = (SpecialBlock) sequBlock.subBlocks[0];
if (optDupX2.type != SpecialBlock.DUP
|| optDupX2.depth != 2)
return false;
params = optDup.count;
params = optDupX2.count;
} else
return false;
}
@ -93,34 +92,36 @@ public class CreateNewConstructor implements Transformation{
&& sequBlock.outer instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.outer;
SpecialBlock dup = null;
if (sequBlock.outer instanceof SequentialBlock
&& sequBlock.subBlocks[0] instanceof SpecialBlock) {
SpecialBlock dup = (SpecialBlock) sequBlock.subBlocks[0];
dup = (SpecialBlock) sequBlock.subBlocks[0];
if (dup.type != SpecialBlock.DUP
|| dup.count != 1 || dup.depth != 0)
return false;
sequBlock = (SequentialBlock)sequBlock.outer;
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
block = (InstructionBlock) sequBlock.subBlocks[0];
if (!(block.getInstruction() instanceof NewOperator))
return false;
}
NewOperator op = (NewOperator) block.getInstruction();
if (constrCall.getClassType() != op.getType())
return false;
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false;
InstructionBlock block = (InstructionBlock) sequBlock.subBlocks[0];
if (!(block.getInstruction() instanceof NewOperator))
return false;
block.removeBlock();
NewOperator op = (NewOperator) block.getInstruction();
if (constrCall.getClassType() != op.getType())
return false;
block.removeBlock();
if (dup != null)
dup.removeBlock();
if (optDup != null)
optDup.depth = 0;
((InstructionContainer) flow.lastModified).setInstruction
(new ConstructorOperator(constrCall.getClassType(),
constrCall.getMethodType()));
return true;
}
return false;
if (optDupX2 != null)
optDupX2.depth = 0;
ic.setInstruction(new ConstructorOperator(constrCall.getClassType(),
constrCall.getMethodType(),
dup == null));
return true;
}
}

@ -20,30 +20,28 @@
package jode.flow;
import jode.*;
public class CreatePrePostIncExpression implements Transformation {
public class CreatePrePostIncExpression {
public boolean transform(FlowBlock flow)
public static boolean transform(InstructionContainer ic,
StructuredBlock last)
{
return (createLocalPrePostInc(flow) || createPostInc(flow));
return (createLocalPrePostInc(ic, last) || createPostInc(ic, last));
}
public boolean createLocalPrePostInc(FlowBlock flow) {
IIncOperator iinc;
boolean isPost;
int op;
InstructionContainer lastBlock;
Type type;
try {
lastBlock = (InstructionContainer) flow.lastModified;
Expression instr2 = lastBlock.getInstruction();
SequentialBlock sequBlock = (SequentialBlock)lastBlock.outer;
if (sequBlock.subBlocks[1] != lastBlock)
return false;
InstructionBlock ib = (InstructionBlock) sequBlock.subBlocks[0];
Expression instr1 = ib.getInstruction();
public static boolean createLocalPrePostInc(InstructionContainer ic,
StructuredBlock last) {
if (last.outer instanceof SequentialBlock
&& last.outer.getSubBlocks()[0] instanceof InstructionBlock) {
Expression instr1 = ((InstructionBlock)
last.outer.getSubBlocks()[0])
.getInstruction();
Expression instr2 = ic.getInstruction();
IIncOperator iinc;
LocalLoadOperator load;
boolean isPost;
if (instr1 instanceof IIncOperator
&& instr2 instanceof LocalLoadOperator) {
iinc = (IIncOperator) instr1;
@ -57,6 +55,7 @@ public class CreatePrePostIncExpression implements Transformation {
} else
return false;
int op;
if (iinc.getOperatorIndex() == iinc.ADD_OP + iinc.OPASSIGN_OP)
op = Operator.INC_OP;
else if (iinc.getOperatorIndex() == iinc.NEG_OP + iinc.OPASSIGN_OP)
@ -72,94 +71,124 @@ public class CreatePrePostIncExpression implements Transformation {
if (!iinc.matches(load))
return false;
type = load.getType().intersection(Type.tUInt);
} catch (NullPointerException ex) {
return false;
} catch (ClassCastException ex) {
return false;
Type type = load.getType().intersection(Type.tUInt);
Operator ppop =
new LocalPrePostFixOperator(type, op, iinc, isPost);
ic.setInstruction(ppop);
ic.moveDefinitions(last.outer, last);
last.replace(last.outer);
return true;
}
Operator ppop = new LocalPrePostFixOperator(type, op, iinc, isPost);
lastBlock.setInstruction(ppop);
lastBlock.moveDefinitions(lastBlock.outer, lastBlock);
lastBlock.replace(lastBlock.outer);
return true;
return false;
}
public boolean createPostInc(FlowBlock flow) {
StoreInstruction store;
int op;
Type type;
InstructionBlock lastBlock;
SequentialBlock sequBlock;
try {
lastBlock = (InstructionBlock) flow.lastModified;
Expression storeExpr = lastBlock.getInstruction();
store = (StoreInstruction) storeExpr.getOperator();
if (!store.isVoid())
return false;
public static boolean createPostInc(InstructionContainer ic,
StructuredBlock last) {
/* Situation:
*
* PUSH load/storeOps PUSH load/storeOps
* DUP load/storeOps PUSH store++/--
* load (unresolved)
* DUP ->
* PUSH +/-1
* IADD/SUB
* store (unresolved)
*
* load (no params) -> PUSH store++/--
* DUP
* PUSH +/-1
* store IADD/SUB
*/
if (!(ic.getInstruction().getOperator() instanceof StoreInstruction)
|| !(ic.getInstruction().isVoid()))
return false;
sequBlock = (SequentialBlock) lastBlock.outer;
if (sequBlock.subBlocks[1] != lastBlock)
return false;
StoreInstruction store =
(StoreInstruction) ic.getInstruction().getOperator();
BinaryOperator binOp;
InstructionBlock ib;
if (store.getLValueOperandCount() > 0) {
ib = (InstructionBlock) sequBlock.subBlocks[0];
binOp = (BinaryOperator) ib.getInstruction();
sequBlock = (SequentialBlock) sequBlock.outer;
} else
binOp = (BinaryOperator)
((ComplexExpression) storeExpr).getSubExpressions()[0];
if (binOp.getOperatorIndex() == store.ADD_OP)
op = Operator.INC_OP;
else if (store.getOperatorIndex() == store.NEG_OP)
op = Operator.DEC_OP;
else
if (!(last.outer instanceof SequentialBlock))
return false;
SequentialBlock sb = (SequentialBlock)last.outer;
Expression binOp;
if (store.getLValueOperandCount() == 0) {
if (!(ic.getInstruction() instanceof ComplexExpression))
return false;
binOp = ((ComplexExpression) ic.getInstruction())
.getSubExpressions()[0];
} else {
if (!(sb.subBlocks[0] instanceof InstructionBlock)
|| !(sb.outer instanceof SequentialBlock))
return false;
binOp = ((InstructionBlock) sb.subBlocks[0])
.getInstruction();
sb = (SequentialBlock) sb.outer;
}
if (!(binOp instanceof BinaryOperator))
return false;
ib = (InstructionBlock) sequBlock.subBlocks[0];
int op;
if (binOp.getOperator().getOperatorIndex() == store.ADD_OP)
op = Operator.INC_OP;
else if (binOp.getOperator().getOperatorIndex() == store.NEG_OP)
op = Operator.DEC_OP;
else
return false;
ConstOperator constOp = (ConstOperator) ib.getInstruction();
if (!constOp.getValue().equals("1") &&
!constOp.getValue().equals("-1"))
return false;
if (constOp.getValue().equals("-1"))
op ^= 1;
if (!(sb.subBlocks[0] instanceof InstructionBlock))
return false;
InstructionBlock ib = (InstructionBlock) sb.subBlocks[0];
if (!(ib.getInstruction() instanceof ConstOperator))
return false;
ConstOperator constOp = (ConstOperator) ib.getInstruction();
if (constOp.getValue().equals("-1"))
op ^= 1;
else if (!constOp.getValue().equals("1"))
return false;
sequBlock = (SequentialBlock) sequBlock.outer;
SpecialBlock dup = (SpecialBlock) sequBlock.subBlocks[0];
if (dup.type != SpecialBlock.DUP
|| dup.count != store.getLValueType().stackSize()
|| dup.depth != store.getLValueOperandCount())
return false;
if (!(sb.outer instanceof SequentialBlock))
return false;
sb = (SequentialBlock) sb.outer;
if (!(sb.subBlocks[0] instanceof SpecialBlock))
return false;
sequBlock = (SequentialBlock) sequBlock.outer;
ib = (InstructionBlock) sequBlock.subBlocks[0];
Operator load = (Operator) ib.getInstruction();
if (!store.matches(load))
return false;
SpecialBlock dup = (SpecialBlock) sb.subBlocks[0];
if (dup.type != SpecialBlock.DUP
|| dup.count != store.getLValueType().stackSize()
|| dup.depth != store.getLValueOperandCount())
return false;
if (store.getLValueOperandCount() > 0) {
sequBlock = (SequentialBlock) sequBlock.outer;
SpecialBlock dup2 = (SpecialBlock) sequBlock.subBlocks[0];
if (dup2.type != SpecialBlock.DUP
|| dup2.count != store.getLValueOperandCount()
|| dup2.depth != 0)
return false;
}
type = load.getType().intersection(store.getLValueType());
} catch (NullPointerException ex) {
if (!(sb.outer instanceof SequentialBlock))
return false;
} catch (ClassCastException ex) {
sb = (SequentialBlock) sb.outer;
if (!(sb.subBlocks[0] instanceof InstructionBlock))
return false;
ib = (InstructionBlock) sb.subBlocks[0];
if (!(ib.getInstruction() instanceof Operator)
|| !store.matches((Operator) ib.getInstruction()))
return false;
if (store.getLValueOperandCount() > 0) {
if (!(sb.outer instanceof SequentialBlock))
return false;
sb = (SequentialBlock) sb.outer;
if (!(sb.subBlocks[0] instanceof SpecialBlock))
return false;
SpecialBlock dup2 = (SpecialBlock) sb.subBlocks[0];
if (dup2.type != SpecialBlock.DUP
|| dup2.count != store.getLValueOperandCount()
|| dup2.depth != 0)
return false;
}
Operator postop = new PrePostFixOperator(type, op, store, true);
lastBlock.setInstruction(postop);
lastBlock.moveDefinitions(sequBlock, lastBlock);
lastBlock.replace(sequBlock);
ic.setInstruction
(new PrePostFixOperator(store.getLValueType(), op, store, true));
ic.moveDefinitions(sb, last);
last.replace(sb);
return true;
}
}

@ -199,73 +199,69 @@ public class FlowBlock {
ConditionalBlock cb = (ConditionalBlock) jump.prev.outer;
Expression instr = cb.getInstruction();
/* If this is the first instruction of a while and the
* condition of the while is true, use the condition
* as while condition.
*/
if (cb.outer instanceof LoopBlock
|| (cb.outer instanceof SequentialBlock
&& cb.outer.getSubBlocks()[0] == cb
&& cb.outer.outer instanceof LoopBlock)) {
LoopBlock loopBlock = (cb.outer instanceof LoopBlock) ?
(LoopBlock) cb.outer : (LoopBlock) cb.outer.outer;
if (loopBlock.getCondition() == LoopBlock.TRUE &&
loopBlock.getType() != LoopBlock.DOWHILE &&
(loopBlock.jumpMayBeChanged()
|| loopBlock.getNextFlowBlock() == succ)) {
if (loopBlock.jump == null) {
/* consider this jump again */
loopBlock.moveJump(jump);
jumps = jump;
} else
jump.prev.removeJump();
loopBlock.setCondition(instr.negate());
loopBlock.moveDefinitions(cb, null);
cb.removeBlock();
continue;
}
} else if (cb.outer instanceof SequentialBlock
&& cb.outer.getSubBlocks()[1] == cb) {
/* And now for do/while loops, where the jump is
* at the end of the loop.
*/
/* First find the beginning of the loop */
StructuredBlock sb = cb.outer.outer;
while (sb instanceof SequentialBlock) {
sb = sb.outer;
}
/* sb is now the first and cb is the last
* instruction in the current block.
*/
if (sb instanceof LoopBlock) {
LoopBlock loopBlock = (LoopBlock) sb;
if (loopBlock.getCondition() == LoopBlock.TRUE &&
loopBlock.getType() == LoopBlock.WHILE &&
(loopBlock.jumpMayBeChanged()
|| loopBlock.getNextFlowBlock() == succ)) {
if (loopBlock.jump == null) {
/* consider this jump again */
loopBlock.moveJump(jump);
jumps = jump;
} else
jump.prev.removeJump();
loopBlock.setType(LoopBlock.DOWHILE);
loopBlock.setCondition(instr.negate());
loopBlock.moveDefinitions(cb, null);
cb.removeBlock();
continue;
}
}
}
// if (cb.outer instanceof LoopBlock
// || (cb.outer instanceof SequentialBlock
// && cb.outer.getSubBlocks()[0] == cb
// && cb.outer.outer instanceof LoopBlock)) {
// LoopBlock loopBlock = (cb.outer instanceof LoopBlock) ?
// (LoopBlock) cb.outer : (LoopBlock) cb.outer.outer;
// if (loopBlock.getCondition() == LoopBlock.TRUE &&
// loopBlock.getType() != LoopBlock.DOWHILE &&
// (loopBlock.jumpMayBeChanged()
// || loopBlock.getNextFlowBlock() == succ)) {
// if (loopBlock.jump == null) {
// /* consider this jump again */
// loopBlock.moveJump(jump);
// jumps = jump;
// } else
// jump.prev.removeJump();
// loopBlock.setCondition(instr.negate());
// loopBlock.moveDefinitions(cb, null);
// cb.removeBlock();
// continue;
// }
// } else if (cb.outer instanceof SequentialBlock
// && cb.outer.getSubBlocks()[1] == cb) {
// /* And now for do/while loops, where the jump is
// * at the end of the loop.
// */
// /* First find the beginning of the loop */
// StructuredBlock sb = cb.outer.outer;
// while (sb instanceof SequentialBlock) {
// sb = sb.outer;
// }
// /* sb is now the first and cb is the last
// * instruction in the current block.
// */
// if (sb instanceof LoopBlock) {
// LoopBlock loopBlock = (LoopBlock) sb;
// if (loopBlock.getCondition() == LoopBlock.TRUE &&
// loopBlock.getType() == LoopBlock.WHILE &&
// (loopBlock.jumpMayBeChanged()
// || loopBlock.getNextFlowBlock() == succ)) {
// if (loopBlock.jump == null) {
// /* consider this jump again */
// loopBlock.moveJump(jump);
// jumps = jump;
// } else
// jump.prev.removeJump();
// loopBlock.setType(LoopBlock.DOWHILE);
// loopBlock.setCondition(instr.negate());
// loopBlock.moveDefinitions(cb, null);
// cb.removeBlock();
// continue;
// }
// }
// }
/* replace all conditional jumps to the successor, which
* are followed by a block which has the end of the block
@ -599,6 +595,30 @@ public class FlowBlock {
}
}
/**
* This is a special T1 transformation, that does also succeed, if
* the jumps in the flow block are not yet resolved. But it has
* a special precondition: The succ must be a simple instruction block,
* mustn't have another predecessor and all structured blocks in this
* flow block must be simple instruction blocks.
*/
public void doSequentialT1(StructuredBlock succ, int length) {
VariableSet succIn = new VariableSet();
succ.fillInGenSet(succIn, this.gen);
succIn.merge(lastModified.jump.gen);
succIn.subtract(lastModified.jump.kill);
succ.jump.gen.mergeGenKill(lastModified.jump.gen, succ.jump.kill);
succ.jump.kill.add(lastModified.jump.kill);
this.in.unionExact(succIn);
lastModified.removeJump();
lastModified = lastModified.appendBlock(succ);
this.length += length;
doTransformations();
}
/**
* Do a T1 transformation with succ if possible. It is possible,
* iff succ has exactly this block as predecessor.
@ -619,19 +639,25 @@ public class FlowBlock {
/* Update the in/out-Vectors now */
updateInOut(succ, jumps);
if (Decompiler.isFlowDebugging)
System.err.println("before Optimize: "+this);
/* Try to eliminate as many jumps as possible.
*/
jumps = optimizeJumps(jumps, succ);
if (Decompiler.isFlowDebugging)
System.err.println("before Remaining: "+this);
resolveRemaining(jumps);
if (Decompiler.isFlowDebugging)
System.err.println("after Optimize: "+this);
/* Now unify the blocks.
*/
lastModified.appendBlock(succ.block);
lastModified = lastModified.appendBlock(succ.block);
mergeSuccessors(succ);
/* Set last modified to the new correct value. */
lastModified = succ.lastModified;
/* This will also set last modified to the new correct value. */
doTransformations();
/* Set addr+length to correct value. */
if (succ.addr < addr)
@ -643,6 +669,44 @@ public class FlowBlock {
return true;
}
/**
* Find the exit condition of a for/while block. The loop block
* mustn't have an exit condition yet.
*/
public void mergeCondition() {
/* If the first instruction of a while is a conditional
* block, which jumps to the next address use the condition
* as while condition.
*/
LoopBlock loopBlock = (LoopBlock) lastModified;
int loopType = loopBlock.getType();
ConditionalBlock cb = null;
if (loopBlock.bodyBlock instanceof ConditionalBlock)
cb = (ConditionalBlock) loopBlock.bodyBlock;
else if (loopBlock.bodyBlock instanceof SequentialBlock
&& loopBlock.bodyBlock.getSubBlocks()[0]
instanceof ConditionalBlock)
cb = (ConditionalBlock) loopBlock.bodyBlock.getSubBlocks()[0];
else if (loopBlock.bodyBlock instanceof SequentialBlock
&& loopType == LoopBlock.WHILE) {
loopType = LoopBlock.DOWHILE;
SequentialBlock sequBlock = (SequentialBlock) loopBlock.bodyBlock;
while (sequBlock.subBlocks[1] instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1];
if (sequBlock.subBlocks[1] instanceof ConditionalBlock)
cb = (ConditionalBlock) sequBlock.subBlocks[1];
}
if (cb != null
&& cb.trueBlock.jump.destination.addr == addr + length) {
loopBlock.moveJump(cb.trueBlock.jump);
loopBlock.setCondition(cb.getInstruction().negate());
loopBlock.setType(loopType);
loopBlock.moveDefinitions(cb, null);
cb.removeBlock();
}
}
public boolean doT2(int start, int end) {
/* If there are no jumps to the beginning of this flow block
@ -800,6 +864,7 @@ public class FlowBlock {
*/
predecessors.removeElement(this);
lastModified = block;
mergeCondition();
/* T2 analysis succeeded */
checkConsistent();
@ -881,20 +946,20 @@ public class FlowBlock {
checkConsistent();
}
public void doTransformations() {
if (Decompiler.isFlowDebugging)
System.err.println("before Transformation: "+this);
static Transformation[] exprTrafos = {
new RemoveEmpty(),
new CreateExpression(),
new CreatePrePostIncExpression(),
new CreateAssignExpression(),
new CreateNewConstructor(),
new CombineIfGotoExpressions(),
new CreateIfThenElseOperator(),
new CreateConstantArray(),
new CreateForInitializer(),
new CompleteSynchronized(),
};
while (lastModified instanceof SequentialBlock) {
if (!lastModified.getSubBlocks()[0].doTransformations())
lastModified = lastModified.getSubBlocks()[1];
}
while (lastModified.doTransformations())
/* empty */;
if (Decompiler.isFlowDebugging)
System.err.println("after Transformation: "+this);
}
/**
* Search for an apropriate successor.
@ -944,21 +1009,12 @@ public class FlowBlock {
while (true) {
if (Decompiler.isFlowDebugging)
System.err.println("before Transformation: "+this);
/* First do some non flow transformations. */
int i=0;
while (i < exprTrafos.length) {
if (exprTrafos[i].transform(this))
i = 0;
else
i++;
checkConsistent();
if (lastModified instanceof SwitchBlock) {
/* analyze the switch first.
*/
analyzeSwitch(start, end);
}
if (Decompiler.isFlowDebugging)
System.err.println("after Transformation: "+this);
if (doT2(start, end)) {
@ -989,20 +1045,6 @@ public class FlowBlock {
+ addr + " - " + (addr+length));
return changed;
} else {
if (succ.block instanceof SwitchBlock) {
/* analyze succ, the new region is the
* continuous region of
* [start,end) \cap \compl [addr, addr+length)
* where succ.addr lies in.
*/
int newStart = (succ.addr > addr)
? addr+length : start;
int newEnd = (succ.addr > addr)
? end : addr;
if (succ.analyzeSwitch(newStart, newEnd))
break;
}
if ((succ.addr == addr+length
|| succ.addr+succ.length == addr)
/* Only do T1 transformation if the blocks are
@ -1058,14 +1100,14 @@ public class FlowBlock {
* regions. Only blocks whose address lies in the given address
* range are considered and it is taken care of, that the switch
* is never leaved. <p>
* The current flow block must contain the switch block as main
* block.
* The current flow block must contain the switch block as lastModified
* @param start the start of the address range.
* @param end the end of the address range.
*/
public boolean analyzeSwitch(int start, int end) {
SwitchBlock switchBlock = (SwitchBlock) block;
SwitchBlock switchBlock = (SwitchBlock) lastModified;
boolean changed = false;
int last = -1;
FlowBlock lastFlow = null;
for (int i=0; i < switchBlock.caseBlocks.length; i++) {
@ -1165,8 +1207,10 @@ public class FlowBlock {
jump.destination = END_OF_METHOD;
else
jump.destination = instr[jump.destAddr];
if (jump.destination == null)
throw new AssertError("Missing dest: "+jump.destAddr);
addSuccessor(jump);
}
addSuccessor(jump);
}
}

@ -56,6 +56,16 @@ public abstract class InstructionContainer extends StructuredBlock {
}
}
public boolean doTransformations() {
StructuredBlock last = flowBlock.lastModified;
return CreateNewConstructor.transform(this, last)
|| CreateExpression.transform(this, last)
|| CreatePrePostIncExpression.transform(this, last)
|| CreateAssignExpression.transform(this, last)
|| CreateIfThenElseOperator.create(this, last)
|| CreateConstantArray.transform(this, last);
}
/**
* Get the contained instruction.
* @return the contained instruction.

@ -253,4 +253,10 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
public boolean jumpMayBeChanged() {
return mayChangeJump;
}
public boolean doTransformations() {
return type == FOR && init == null
&& CreateForInitializer.transform(this, flowBlock.lastModified);
}
}

@ -21,41 +21,10 @@ package jode.flow;
import jode.Expression;
import jode.NopOperator;
public class RemoveEmpty implements Transformation {
public class RemoveEmpty {
public boolean transform (FlowBlock fb) {
return (removeNop(fb) || removeSwap(fb) || removeEmpty(fb));
}
public boolean removeNop(FlowBlock flow) {
StructuredBlock block = flow.lastModified;
if (block instanceof InstructionContainer
&& block.outer instanceof SequentialBlock
&& block.outer.getSubBlocks()[0] instanceof InstructionBlock) {
InstructionContainer ic = (InstructionContainer) block;
Expression nopInstr = ic.getInstruction();
if (!(nopInstr instanceof NopOperator)
|| nopInstr.getType() == jode.Type.tVoid)
return false;
InstructionBlock prev =
(InstructionBlock) ic.outer.getSubBlocks()[0];
Expression instr = prev.getInstruction();
if (instr.getType() == jode.Type.tVoid)
return false;
instr.setType(nopInstr.getType());
ic.setInstruction(instr);
ic.replace(ic.outer);
return true;
}
return false;
}
public boolean removeSwap(FlowBlock flow) {
public static boolean removeSwap(SpecialBlock swapBlock,
StructuredBlock last) {
/* Remove non needed swaps; convert:
*
* PUSH expr1
@ -67,37 +36,43 @@ public class RemoveEmpty implements Transformation {
* PUSH expr2
* PUSH expr1
*/
StructuredBlock block = flow.lastModified;
if (block instanceof SpecialBlock
&& ((SpecialBlock)block).type == SpecialBlock.SWAP
&& block.outer instanceof SequentialBlock
&& block.outer.outer instanceof SequentialBlock
&& block.outer.getSubBlocks()[0] instanceof InstructionBlock
&& block.outer.outer.getSubBlocks()[0]
if (last.outer instanceof SequentialBlock
&& last.outer.outer instanceof SequentialBlock
&& last.outer.getSubBlocks()[0] instanceof InstructionBlock
&& last.outer.outer.getSubBlocks()[0]
instanceof InstructionBlock) {
InstructionBlock block1
= (InstructionBlock) block.outer.outer.getSubBlocks()[0];
= (InstructionBlock) last.outer.outer.getSubBlocks()[0];
InstructionBlock block2
= (InstructionBlock) block.outer.getSubBlocks()[0];
= (InstructionBlock) last.outer.getSubBlocks()[0];
/* XXX check if blocks may be swapped
* (there mustn't be side effects in one of them).
*/
System.err.println("WARNING: this program contains a SWAP "
+"opcode and may not be translated correctly.");
if (block1.getInstruction().isVoid()
|| block2.getInstruction().isVoid())
return false;
/* PUSH expr1 == block1
* PUSH expr2
* SWAP
* ...
*/
block.outer.replace(block1.outer);
last.outer.replace(block1.outer);
/* PUSH expr2
* SWAP
* ...
*/
block1.replace(block);
block1.moveJump(block.jump);
block1.replace(swapBlock);
block1.moveJump(swapBlock.jump);
/* PUSH expr2
* PUSH expr1
*/
flow.lastModified = block1;
block1.flowBlock.lastModified = block1.outer;
return true;
}
return false;

@ -56,4 +56,9 @@ public class SpecialBlock extends StructuredBlock {
+ ((count == 1) ? "" : "2")
+ ((depth == 0) ? "" : "_X"+depth));
}
public boolean doTransformation() {
return type == SWAP
&& RemoveEmpty.removeSwap(this, flowBlock.lastModified);
}
}

@ -191,7 +191,7 @@ public abstract class StructuredBlock {
/**
* Removes the jump. This does not update the successors vector
* of the flow block, you have to do it yourself. */
public void removeJump() {
public final void removeJump() {
if (jump != null) {
jump.prev = null;
jump = null;
@ -208,21 +208,15 @@ public abstract class StructuredBlock {
* will be moved to this block (may be this).
*/
void moveDefinitions(StructuredBlock from, StructuredBlock sub) {
if (from != sub && from != this) {
/* define(...) will not move from blocks, that are not sub blocks,
* so we do it by hand.
*/
java.util.Enumeration enum = from.used.elements();
while (enum.hasMoreElements()) {
LocalInfo var =
((LocalInfo) enum.nextElement()).getLocalInfo();
if (!used.contains(var))
used.addElement(var);
}
while (from != sub && from != this) {
used.unionExact(from.used);
from.used.removeAllElements();
StructuredBlock[] subs = from.getSubBlocks();
for (int i=0; i<subs.length; i++)
if (subs.length == 0)
return;
for (int i=0; i<subs.length - 1; i++)
moveDefinitions(subs[i], sub);
from = subs[subs.length-1];
}
}
@ -282,11 +276,16 @@ public abstract class StructuredBlock {
* @return the new combined block.
*/
public StructuredBlock appendBlock(StructuredBlock block) {
SequentialBlock sequBlock = new SequentialBlock();
sequBlock.replace(this);
sequBlock.setFirst(this);
sequBlock.setSecond(block);
return sequBlock;
if (block instanceof EmptyBlock) {
moveJump(block.jump);
return this;
} else {
SequentialBlock sequBlock = new SequentialBlock();
sequBlock.replace(this);
sequBlock.setFirst(this);
sequBlock.setSecond(block);
return sequBlock;
}
}
/**
@ -318,14 +317,6 @@ public abstract class StructuredBlock {
return false;
}
public void define(VariableSet vars) {
java.util.Enumeration enum = vars.elements();
while (enum.hasMoreElements()) {
LocalInfo var = ((LocalInfo) enum.nextElement()).getLocalInfo();
used.addElement(var);
}
}
public VariableSet propagateUsage() {
StructuredBlock[] subs = getSubBlocks();
VariableSet allUse = (VariableSet) used.clone();
@ -371,7 +362,7 @@ public abstract class StructuredBlock {
}
subs[i].checkConsistent();
}
if (jump != null) {
if (jump != null && jump.destination != null) {
Jump jumps = (Jump) flowBlock.successors.get(jump.destination);
for (; jumps != jump; jumps = jumps.next) {
if (jumps == null)
@ -484,5 +475,12 @@ public abstract class StructuredBlock {
return super.toString();
}
}
/**
* Do simple transformation on the structuredBlock.
*/
public boolean doTransformations() {
return false;
}
}

@ -91,4 +91,8 @@ public class SynchronizedBlock extends StructuredBlock {
writer.untab();
writer.println("}");
}
public boolean doTransformations() {
return CompleteSynchronized.transform(this, flowBlock.lastModified);
}
}

@ -192,7 +192,7 @@ public class ClassInterfacesType extends Type {
return tError;
ClassInterfacesType other = (ClassInterfacesType) type;
Class clazz = null;
Class clazz;
Vector ifaces = new Vector();
/* First determine the clazz, one of the two classes must be a sub

Loading…
Cancel
Save