You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
15 KiB
529 lines
15 KiB
26 years ago
|
/* LoopBlock Copyright (C) 1998-1999 Jochen Hoenicke.
|
||
26 years ago
|
*
|
||
26 years ago
|
* 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.
|
||
26 years ago
|
*
|
||
26 years ago
|
* 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.
|
||
26 years ago
|
*
|
||
26 years ago
|
* 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.
|
||
26 years ago
|
*
|
||
|
* $Id$
|
||
|
*/
|
||
|
|
||
|
package jode.flow;
|
||
26 years ago
|
import jode.decompiler.TabbedPrintWriter;
|
||
26 years ago
|
import jode.type.Type;
|
||
26 years ago
|
import jode.decompiler.LocalInfo;
|
||
26 years ago
|
import jode.expr.Expression;
|
||
|
import jode.expr.ConstOperator;
|
||
25 years ago
|
import jode.expr.StoreInstruction;
|
||
26 years ago
|
import jode.expr.LocalStoreOperator;
|
||
|
import jode.expr.CombineableOperator;
|
||
25 years ago
|
import jode.util.SimpleSet;
|
||
26 years ago
|
|
||
25 years ago
|
import @COLLECTIONS@.Set;
|
||
25 years ago
|
|
||
26 years ago
|
/**
|
||
|
* This is the structured block for an Loop block.
|
||
|
*/
|
||
26 years ago
|
public class LoopBlock extends StructuredBlock implements BreakableBlock {
|
||
26 years ago
|
|
||
|
public static final int WHILE = 0;
|
||
|
public static final int DOWHILE = 1;
|
||
|
public static final int FOR = 2;
|
||
26 years ago
|
public static final int POSSFOR = 3;
|
||
26 years ago
|
|
||
26 years ago
|
public static final Expression TRUE =
|
||
26 years ago
|
new ConstOperator(Boolean.TRUE);
|
||
26 years ago
|
public static final Expression FALSE =
|
||
26 years ago
|
new ConstOperator(Boolean.FALSE);
|
||
26 years ago
|
|
||
|
/**
|
||
|
* The condition. Must be of boolean type.
|
||
|
*/
|
||
26 years ago
|
Expression cond;
|
||
26 years ago
|
/**
|
||
|
* The stack the condition eats.
|
||
|
*/
|
||
|
VariableStack condStack;
|
||
26 years ago
|
/**
|
||
25 years ago
|
* The init instruction block, only valid if type == POSSFOR
|
||
26 years ago
|
*/
|
||
25 years ago
|
InstructionBlock initBlock;
|
||
26 years ago
|
/**
|
||
25 years ago
|
* The increase instruction block, only valid if type == POSSFOR.
|
||
26 years ago
|
*/
|
||
25 years ago
|
InstructionBlock incrBlock;
|
||
|
|
||
|
/**
|
||
|
* The init instruction, only valid if type == FOR.
|
||
|
*/
|
||
|
Expression initInstr;
|
||
|
/**
|
||
|
* The increase instruction, only valid if type == FOR.
|
||
|
*/
|
||
|
Expression incrInstr;
|
||
26 years ago
|
|
||
26 years ago
|
/**
|
||
|
* True, if the initializer is a declaration.
|
||
|
*/
|
||
|
boolean isDeclaration;
|
||
|
|
||
26 years ago
|
/**
|
||
|
* The type of the loop. This must be one of DOWHILE, WHILE or FOR.
|
||
|
*/
|
||
|
int type;
|
||
|
|
||
|
/**
|
||
26 years ago
|
* The body of this loop. This is always a valid block and not null.
|
||
26 years ago
|
*/
|
||
|
StructuredBlock bodyBlock;
|
||
|
|
||
26 years ago
|
/**
|
||
|
* The stack after the break.
|
||
|
*/
|
||
|
VariableStack breakedStack;
|
||
|
|
||
|
/**
|
||
|
* The stack at begin of the loop.
|
||
|
*/
|
||
|
VariableStack continueStack;
|
||
|
|
||
25 years ago
|
/*{ invariant { type == POSSFOR || (incrBlock == null && initBlock == null)
|
||
25 years ago
|
:: "(while/do while) with incr";
|
||
25 years ago
|
type == FOR || (incrInstr == null && initInstr == null)
|
||
25 years ago
|
:: "(while/do while/poss for) with init";
|
||
25 years ago
|
type != POSSFOR || incrBlock != null
|
||
|
:: "possible for without incr";
|
||
|
type != FOR || incrInstr != null
|
||
|
:: "for without incr";
|
||
25 years ago
|
type != POSSFOR ||
|
||
25 years ago
|
incrBlock.getInstruction() instanceof CombineableOperator
|
||
25 years ago
|
:: "possible for with invalid incr";
|
||
25 years ago
|
initBlock == null ||
|
||
|
(initBlock.getInstruction() instanceof CombinableOperator)
|
||
|
:: "Initializer is not combinableOperator";
|
||
|
initInstr == null ||
|
||
|
(initInstr instanceof CombinableOperator)
|
||
26 years ago
|
:: "Initializer is not combinableOperator";
|
||
|
cond != null && cond.getType() == Type.tBoolean
|
||
|
:: "invalid condition type";
|
||
|
type != POSSFOR || bodyBlock.contains(incr)
|
||
|
:: "incr is not in body of possible for" } }*/
|
||
|
|
||
26 years ago
|
/**
|
||
|
* Returns the block where the control will normally flow to, when
|
||
|
* the given sub block is finished (<em>not</em> ignoring the jump
|
||
|
* after this block). (This is overwritten by SequentialBlock and
|
||
|
* SwitchBlock). If this isn't called with a direct sub block,
|
||
|
* the behaviour is undefined, so take care.
|
||
|
* @return null, if the control flows to another FlowBlock. */
|
||
|
public StructuredBlock getNextBlock(StructuredBlock subBlock) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public FlowBlock getNextFlowBlock(StructuredBlock subBlock) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
26 years ago
|
public LoopBlock(int type, Expression cond) {
|
||
26 years ago
|
this.type = type;
|
||
|
this.cond = cond;
|
||
|
this.mayChangeJump = (cond == TRUE);
|
||
|
}
|
||
|
|
||
|
public void setBody(StructuredBlock body) {
|
||
|
bodyBlock = body;
|
||
|
bodyBlock.outer = this;
|
||
|
body.setFlowBlock(flowBlock);
|
||
|
}
|
||
|
|
||
25 years ago
|
public void setInit(InstructionBlock initBlock) {
|
||
|
if (type == POSSFOR) {
|
||
|
this.initBlock = initBlock;
|
||
|
} else if (type == FOR) {
|
||
|
this.initInstr = initBlock.getInstruction();
|
||
|
initBlock.removeBlock();
|
||
|
}
|
||
26 years ago
|
}
|
||
|
|
||
25 years ago
|
public boolean conditionMatches(CombineableOperator combinable) {
|
||
|
return (type == POSSFOR || cond.containsMatchingLoad(combinable));
|
||
26 years ago
|
}
|
||
26 years ago
|
|
||
26 years ago
|
|
||
|
public Expression getCondition() {
|
||
|
return cond;
|
||
26 years ago
|
}
|
||
|
|
||
26 years ago
|
public void setCondition(Expression cond) {
|
||
26 years ago
|
this.cond = cond;
|
||
26 years ago
|
if (type == POSSFOR) {
|
||
26 years ago
|
/* We can now say, if this is a for block or not.
|
||
|
*/
|
||
25 years ago
|
if (cond.containsMatchingLoad((CombineableOperator)
|
||
25 years ago
|
incrBlock.getInstruction())) {
|
||
26 years ago
|
type = FOR;
|
||
25 years ago
|
incrInstr = incrBlock.getInstruction();
|
||
|
incrBlock.removeBlock();
|
||
|
if (initBlock != null) {
|
||
|
if (cond.containsMatchingLoad
|
||
|
((CombineableOperator) initBlock.getInstruction())) {
|
||
|
initInstr = initBlock.getInstruction();
|
||
|
initBlock.removeBlock();
|
||
|
}
|
||
26 years ago
|
}
|
||
|
} else {
|
||
26 years ago
|
/* This is not a for block, as it seems first. Make
|
||
|
* it a while block again, and forget about init and
|
||
|
* incr. */
|
||
26 years ago
|
type = WHILE;
|
||
|
}
|
||
25 years ago
|
initBlock = incrBlock = null;
|
||
26 years ago
|
}
|
||
26 years ago
|
mayChangeJump = false;
|
||
|
}
|
||
|
|
||
|
public int getType() {
|
||
|
return type;
|
||
|
}
|
||
|
|
||
|
public void setType(int type) {
|
||
|
this.type = type;
|
||
|
}
|
||
|
|
||
25 years ago
|
/**
|
||
|
* Remove all variables from set, that we can declare inside the
|
||
|
* loop-block. This is the initializer for for-blocks.
|
||
|
*/
|
||
25 years ago
|
public void removeLocallyDeclareable(Set set) {
|
||
25 years ago
|
if (type == FOR && initInstr instanceof StoreInstruction) {
|
||
|
StoreInstruction storeOp = (StoreInstruction) initInstr;
|
||
25 years ago
|
if (storeOp.getLValue() instanceof LocalStoreOperator) {
|
||
|
LocalInfo local =
|
||
|
((LocalStoreOperator) storeOp.getLValue()).getLocalInfo();
|
||
25 years ago
|
set.remove(local);
|
||
25 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
25 years ago
|
public Set getDeclarables() {
|
||
|
Set used = new SimpleSet();
|
||
25 years ago
|
if (type == FOR) {
|
||
25 years ago
|
incrInstr.fillDeclarables(used);
|
||
25 years ago
|
if (initInstr != null)
|
||
25 years ago
|
initInstr.fillDeclarables(used);
|
||
25 years ago
|
}
|
||
25 years ago
|
cond.fillDeclarables(used);
|
||
25 years ago
|
return used;
|
||
26 years ago
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
|
* Replaces the given sub block with a new block.
|
||
|
* @param oldBlock the old sub block.
|
||
|
* @param newBlock the new sub block.
|
||
|
* @return false, if oldBlock wasn't a direct sub block.
|
||
|
*/
|
||
|
public boolean replaceSubBlock(StructuredBlock oldBlock,
|
||
26 years ago
|
StructuredBlock newBlock) {
|
||
26 years ago
|
if (bodyBlock == oldBlock)
|
||
|
bodyBlock = newBlock;
|
||
|
else
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns all sub block of this structured block.
|
||
|
*/
|
||
|
public StructuredBlock[] getSubBlocks() {
|
||
26 years ago
|
return new StructuredBlock[] { bodyBlock };
|
||
|
}
|
||
|
|
||
25 years ago
|
/**
|
||
|
* Check if this is an local store instruction to a not yet declared
|
||
|
* variable. In that case mark this as declaration and return the
|
||
|
* variable.
|
||
|
*/
|
||
25 years ago
|
public void checkDeclaration(Set declareSet) {
|
||
25 years ago
|
if (initInstr instanceof StoreInstruction
|
||
|
&& (((StoreInstruction)initInstr).getLValue()
|
||
|
instanceof LocalStoreOperator)) {
|
||
|
StoreInstruction storeOp = (StoreInstruction) initInstr;
|
||
|
LocalInfo local =
|
||
|
((LocalStoreOperator) storeOp.getLValue()).getLocalInfo();
|
||
|
if (declareSet.contains(local)) {
|
||
|
/* Special case: This is a variable assignment, and
|
||
|
* the variable has not been declared before. We can
|
||
|
* change this to a initializing variable declaration.
|
||
|
*/
|
||
|
isDeclaration = true;
|
||
|
storeOp.getSubExpressions()[1].makeInitializer();
|
||
25 years ago
|
declareSet.remove(local);
|
||
25 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
|
* Make the declarations, i.e. initialize the declare variable
|
||
|
* to correct values. This will declare every variable that
|
||
|
* is marked as used, but not done.
|
||
|
* @param done The set of the already declare variables.
|
||
|
*/
|
||
25 years ago
|
public void makeDeclaration(Set done) {
|
||
26 years ago
|
super.makeDeclaration(done);
|
||
25 years ago
|
if (type == FOR && initInstr != null)
|
||
|
checkDeclaration(declare);
|
||
26 years ago
|
}
|
||
|
|
||
|
public void dumpSource(TabbedPrintWriter writer)
|
||
|
throws java.io.IOException
|
||
|
{
|
||
|
super.dumpSource(writer);
|
||
26 years ago
|
}
|
||
|
|
||
|
public void dumpInstruction(TabbedPrintWriter writer)
|
||
|
throws java.io.IOException
|
||
|
{
|
||
26 years ago
|
if (label != null) {
|
||
|
writer.untab();
|
||
|
writer.println(label+":");
|
||
|
writer.tab();
|
||
|
}
|
||
26 years ago
|
boolean needBrace = bodyBlock.needsBraces();
|
||
26 years ago
|
switch (type) {
|
||
26 years ago
|
case POSSFOR:
|
||
|
/* a possible for is now treated like a WHILE */
|
||
26 years ago
|
case WHILE:
|
||
26 years ago
|
if (cond == TRUE)
|
||
|
/* special syntax for endless loops: */
|
||
|
writer.print("for (;;)");
|
||
26 years ago
|
else {
|
||
|
writer.print("while (");
|
||
|
cond.dumpExpression(writer);
|
||
|
writer.print(")");
|
||
|
}
|
||
26 years ago
|
break;
|
||
|
case DOWHILE:
|
||
|
writer.print("do");
|
||
|
break;
|
||
|
case FOR:
|
||
26 years ago
|
writer.print("for (");
|
||
25 years ago
|
if (initInstr != null) {
|
||
|
if (isDeclaration) {
|
||
25 years ago
|
writer.printType(((LocalStoreOperator)
|
||
25 years ago
|
((CombineableOperator) initInstr)
|
||
|
.getLValue())
|
||
26 years ago
|
.getLocalInfo().getType().getHint());
|
||
|
writer.print(" ");
|
||
|
}
|
||
25 years ago
|
initInstr.dumpExpression(writer);
|
||
26 years ago
|
} else
|
||
|
writer.print("/**/");
|
||
26 years ago
|
writer.print("; ");
|
||
|
cond.dumpExpression(writer);
|
||
|
writer.print("; ");
|
||
25 years ago
|
incrInstr.dumpExpression(writer);
|
||
26 years ago
|
writer.print(")");
|
||
26 years ago
|
break;
|
||
|
}
|
||
26 years ago
|
if (needBrace)
|
||
|
writer.openBrace();
|
||
|
else
|
||
|
writer.println();
|
||
26 years ago
|
writer.tab();
|
||
|
bodyBlock.dumpSource(writer);
|
||
|
writer.untab();
|
||
26 years ago
|
if (type == DOWHILE) {
|
||
|
if (needBrace)
|
||
|
writer.closeBraceContinue();
|
||
26 years ago
|
writer.print("while (");
|
||
|
cond.dumpExpression(writer);
|
||
|
writer.println(");");
|
||
26 years ago
|
} else if (needBrace)
|
||
|
writer.closeBrace();
|
||
26 years ago
|
}
|
||
26 years ago
|
|
||
|
|
||
|
boolean mayChangeJump = true;
|
||
|
|
||
|
/**
|
||
|
* The serial number for labels.
|
||
|
*/
|
||
|
static int serialno = 0;
|
||
|
|
||
|
/**
|
||
|
* The label of this instruction, or null if it needs no label.
|
||
|
*/
|
||
|
String label = null;
|
||
|
|
||
|
/**
|
||
|
* Returns the label of this block and creates a new label, if
|
||
|
* there wasn't a label previously.
|
||
|
*/
|
||
|
public String getLabel() {
|
||
|
if (label == null)
|
||
|
label = "while_"+(serialno++)+"_";
|
||
|
return label;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is called by BreakBlock, to tell us that this block is breaked.
|
||
|
*/
|
||
|
public void setBreaked() {
|
||
|
mayChangeJump = false;
|
||
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
|
* This is called after the analysis is completely done. It
|
||
|
* will remove all PUSH/stack_i expressions, (if the bytecode
|
||
|
* is correct).
|
||
|
* @param stack the stack at begin of the block
|
||
|
* @return null if there is no way to the end of this block,
|
||
|
* otherwise the stack after the block has executed.
|
||
|
*/
|
||
|
public VariableStack mapStackToLocal(VariableStack stack) {
|
||
26 years ago
|
if (type == DOWHILE) {
|
||
|
VariableStack afterBody = bodyBlock.mapStackToLocal(stack);
|
||
|
if (afterBody != null)
|
||
|
mergeContinueStack(afterBody);
|
||
|
|
||
|
if (continueStack != null) {
|
||
|
VariableStack newStack;
|
||
25 years ago
|
int params = cond.getFreeOperandCount();
|
||
26 years ago
|
if (params > 0) {
|
||
|
condStack = continueStack.peek(params);
|
||
|
newStack = continueStack.pop(params);
|
||
|
} else
|
||
|
newStack = continueStack;
|
||
|
|
||
|
if (cond != TRUE)
|
||
|
mergeBreakedStack(newStack);
|
||
26 years ago
|
if (cond != FALSE)
|
||
|
stack.merge(newStack);
|
||
26 years ago
|
}
|
||
|
} else {
|
||
|
continueStack = stack;
|
||
|
VariableStack newStack;
|
||
25 years ago
|
int params = cond.getFreeOperandCount();
|
||
26 years ago
|
if (params > 0) {
|
||
|
condStack = stack.peek(params);
|
||
|
newStack = stack.pop(params);
|
||
|
} else
|
||
|
newStack = stack;
|
||
|
if (cond != TRUE)
|
||
|
breakedStack = newStack;
|
||
|
VariableStack afterBody = bodyBlock.mapStackToLocal(newStack);
|
||
|
if (afterBody != null)
|
||
|
mergeContinueStack(afterBody);
|
||
|
}
|
||
26 years ago
|
return breakedStack;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Is called by BreakBlock, to tell us what the stack can be after a
|
||
|
* break.
|
||
|
* @return false if the stack is inconsistent.
|
||
|
*/
|
||
|
public void mergeContinueStack(VariableStack stack) {
|
||
26 years ago
|
if (continueStack == null)
|
||
|
continueStack = stack;
|
||
|
else
|
||
|
continueStack.merge(stack);
|
||
26 years ago
|
}
|
||
|
|
||
|
/**
|
||
|
* Is called by BreakBlock, to tell us what the stack can be after a
|
||
|
* break.
|
||
|
* @return false if the stack is inconsistent.
|
||
|
*/
|
||
|
public void mergeBreakedStack(VariableStack stack) {
|
||
|
if (breakedStack != null)
|
||
|
breakedStack.merge(stack);
|
||
|
else
|
||
|
breakedStack = stack;
|
||
|
}
|
||
|
|
||
|
public void removePush() {
|
||
|
if (condStack != null)
|
||
25 years ago
|
cond = condStack.mergeIntoExpression(cond);
|
||
26 years ago
|
bodyBlock.removePush();
|
||
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
|
* This method should remove local variables that are only written
|
||
|
* and read one time directly after another. <br>
|
||
|
*
|
||
|
* This is especially important for stack locals, that are created
|
||
|
* when there are unusual swap or dup instructions, but also makes
|
||
|
* inlined functions more pretty (but not that close to the
|
||
|
* bytecode).
|
||
|
*/
|
||
|
public void removeOnetimeLocals() {
|
||
|
cond = cond.removeOnetimeLocals();
|
||
|
if (type == FOR) {
|
||
25 years ago
|
if (initInstr != null)
|
||
|
initInstr.removeOnetimeLocals();
|
||
|
incrInstr.removeOnetimeLocals();
|
||
26 years ago
|
}
|
||
|
super.removeOnetimeLocals();
|
||
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
26 years ago
|
* Replace all breaks to block with a continue to this.
|
||
|
* @param block the breakable block where the breaks originally
|
||
|
* breaked to (Have a break now, if you didn't understand that :-).
|
||
26 years ago
|
*/
|
||
26 years ago
|
public void replaceBreakContinue(BreakableBlock block) {
|
||
26 years ago
|
java.util.Stack todo = new java.util.Stack();
|
||
26 years ago
|
todo.push(block);
|
||
26 years ago
|
while (!todo.isEmpty()) {
|
||
|
StructuredBlock[] subs =
|
||
|
((StructuredBlock)todo.pop()).getSubBlocks();
|
||
|
for (int i=0; i<subs.length; i++) {
|
||
|
if (subs[i] instanceof BreakBlock) {
|
||
|
BreakBlock breakblk = (BreakBlock) subs[i];
|
||
26 years ago
|
if (breakblk.breaksBlock == block) {
|
||
26 years ago
|
new ContinueBlock(this, breakblk.label != null)
|
||
26 years ago
|
.replace(breakblk);
|
||
26 years ago
|
}
|
||
|
}
|
||
|
todo.push(subs[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
26 years ago
|
/**
|
||
|
* Determines if there is a sub block, that flows through to the end
|
||
|
* of this block. If this returns true, you know that jump is null.
|
||
|
* @return true, if the jump may be safely changed.
|
||
|
*/
|
||
|
public boolean jumpMayBeChanged() {
|
||
|
return mayChangeJump;
|
||
|
}
|
||
26 years ago
|
|
||
26 years ago
|
public void simplify() {
|
||
|
cond = cond.simplify();
|
||
|
if (type == FOR) {
|
||
25 years ago
|
incrInstr = incrInstr.simplify();
|
||
25 years ago
|
if (initInstr != null)
|
||
25 years ago
|
initInstr = initInstr.simplify();
|
||
26 years ago
|
}
|
||
|
super.simplify();
|
||
|
}
|
||
|
|
||
26 years ago
|
public boolean doTransformations() {
|
||
25 years ago
|
return ((initBlock == null && type == POSSFOR)
|
||
|
|| (initInstr == null && type == FOR))
|
||
26 years ago
|
&& CreateForInitializer.transform(this, flowBlock.lastModified);
|
||
|
}
|
||
26 years ago
|
}
|
||
26 years ago
|
|