Mirror of the JODE repository
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.
 
 
 
 
 
 
jode/jode/src/net/sf/jode/flow/LoopBlock.java

546 lines
16 KiB

/* LoopBlock Copyright (C) 1998-2002 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser General Public License
* along with this program; see the file COPYING.LESSER. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.flow;
import net.sf.jode.decompiler.TabbedPrintWriter;
import net.sf.jode.decompiler.LocalInfo;
import net.sf.jode.expr.Expression;
import net.sf.jode.expr.ConstOperator;
import net.sf.jode.expr.StoreInstruction;
import net.sf.jode.expr.LocalStoreOperator;
import net.sf.jode.expr.CombineableOperator;
import net.sf.jode.util.SimpleSet;
///#def COLLECTIONS java.util
import java.util.Set;
///#enddef
/**
* This is the structured block for an Loop block.
*/
public class LoopBlock extends StructuredBlock implements BreakableBlock {
public static final int WHILE = 0;
public static final int DOWHILE = 1;
public static final int FOR = 2;
public static final int POSSFOR = 3;
public static final Expression TRUE =
new ConstOperator(Boolean.TRUE);
public static final Expression FALSE =
new ConstOperator(Boolean.FALSE);
/**
* The condition. Must be of boolean type.
*/
Expression cond;
/**
* The stack the condition eats.
*/
VariableStack condStack;
/**
* The init instruction block, only valid if type == POSSFOR
*/
InstructionBlock initBlock;
/**
* The increase instruction block, only valid if type == POSSFOR.
*/
InstructionBlock incrBlock;
/**
* The init instruction, only valid if type == FOR.
*/
Expression initInstr;
/**
* The increase instruction, only valid if type == FOR.
*/
Expression incrInstr;
/**
* True, if the initializer is a declaration.
*/
boolean isDeclaration;
/**
* The type of the loop. This must be one of DOWHILE, WHILE or FOR.
*/
int type;
/**
* The body of this loop. This is always a valid block and not null.
*/
StructuredBlock bodyBlock;
/**
* The stack after the break.
*/
VariableStack breakedStack;
/**
* The stack at begin of the loop.
*/
VariableStack continueStack;
/*{ invariant { type == POSSFOR || (incrBlock == null && initBlock == null)
:: "(while/do while) with incr";
type == FOR || (incrInstr == null && initInstr == null)
:: "(while/do while/poss for) with init";
type != POSSFOR || incrBlock != null
:: "possible for without incr";
type != FOR || incrInstr != null
:: "for without incr";
type != POSSFOR ||
incrBlock.getInstruction() instanceof CombineableOperator
:: "possible for with invalid incr";
initBlock == null ||
(initBlock.getInstruction() instanceof CombinableOperator)
:: "Initializer is not combinableOperator";
initInstr == null ||
(initInstr instanceof CombinableOperator)
:: "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" } }*/
/**
* 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;
}
public LoopBlock(int type, Expression cond) {
this.type = type;
this.cond = cond;
this.mayChangeJump = (cond == TRUE);
}
public void setBody(StructuredBlock body) {
bodyBlock = body;
bodyBlock.outer = this;
body.setFlowBlock(flowBlock);
}
public void setInit(InstructionBlock initBlock) {
if (type == POSSFOR) {
this.initBlock = initBlock;
} else if (type == FOR) {
this.initInstr = initBlock.getInstruction();
initBlock.removeBlock();
}
}
public boolean conditionMatches(CombineableOperator combinable) {
return (type == POSSFOR || cond.containsMatchingLoad(combinable));
}
public Expression getCondition() {
return cond;
}
public void setCondition(Expression cond) {
this.cond = cond;
if (type == POSSFOR) {
/* We can now say, if this is a for block or not.
*/
if (cond.containsMatchingLoad((CombineableOperator)
incrBlock.getInstruction())) {
type = FOR;
incrInstr = incrBlock.getInstruction();
incrBlock.removeBlock();
if (initBlock != null) {
if (cond.containsMatchingLoad
((CombineableOperator) initBlock.getInstruction())) {
initInstr = initBlock.getInstruction();
initBlock.removeBlock();
}
}
} else {
/* This is not a for block, as it seems first. Make
* it a while block again, and forget about init and
* incr. */
type = WHILE;
}
initBlock = incrBlock = null;
}
mayChangeJump = false;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
/**
* 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,
StructuredBlock newBlock) {
if (bodyBlock == oldBlock)
bodyBlock = newBlock;
else
return false;
newBlock.outer = this;
oldBlock.outer = null;
return true;
}
/**
* Returns all sub block of this structured block.
*/
public StructuredBlock[] getSubBlocks() {
return new StructuredBlock[] { bodyBlock };
}
/**
* Remove all variables from set, that we can declare inside the
* loop-block. This is the initializer for for-blocks.
*/
public void removeLocallyDeclareable(Set set) {
if (type == FOR && initInstr instanceof StoreInstruction) {
StoreInstruction storeOp = (StoreInstruction) initInstr;
if (storeOp.getLValue() instanceof LocalStoreOperator) {
LocalInfo local =
((LocalStoreOperator) storeOp.getLValue()).getLocalInfo();
set.remove(local);
}
}
}
public Set getDeclarables() {
Set used = new SimpleSet();
if (type == FOR) {
incrInstr.fillDeclarables(used);
if (initInstr != null)
initInstr.fillDeclarables(used);
}
cond.fillDeclarables(used);
return used;
}
/**
* 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.
*/
public void checkDeclaration(Set declareSet) {
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;
declareSet.remove(local);
}
}
}
/**
* 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.
*/
public void makeDeclaration(Set done) {
if (type == FOR) {
if (initInstr != null)
initInstr.makeDeclaration(done);
incrInstr.makeDeclaration(done);
}
cond.makeDeclaration(done);
super.makeDeclaration(done);
if (type == FOR && initInstr != null)
checkDeclaration(declare);
}
public void dumpSource(TabbedPrintWriter writer)
throws java.io.IOException
{
super.dumpSource(writer);
}
public void dumpInstruction(TabbedPrintWriter writer)
throws java.io.IOException
{
if (label != null) {
writer.untab();
writer.println(label+":");
writer.tab();
}
boolean needBrace = bodyBlock.needsBraces();
switch (type) {
case POSSFOR:
/* a possible for is now treated like a WHILE */
case WHILE:
if (cond == TRUE)
/* special syntax for endless loops: */
writer.print("for (;;)");
else {
writer.print("while (");
cond.dumpExpression(TabbedPrintWriter.EXPL_PAREN, writer);
writer.print(")");
}
break;
case DOWHILE:
writer.print("do");
break;
case FOR:
writer.print("for (");
writer.startOp(TabbedPrintWriter.EXPL_PAREN, 0);
if (initInstr != null) {
if (isDeclaration) {
StoreInstruction store = (StoreInstruction) initInstr;
LocalInfo local = ((LocalStoreOperator) store
.getLValue()).getLocalInfo();
writer.startOp(TabbedPrintWriter.NO_PAREN, 1);
local.dumpDeclaration(writer);
writer.breakOp();
writer.print(" = ");
store.getSubExpressions()[1]
.makeInitializer(local.getType());
store.getSubExpressions()[1].dumpExpression(writer, 100);
writer.endOp();
} else
initInstr.dumpExpression(TabbedPrintWriter.NO_PAREN, writer);
} else {
writer.print("/**/");
}
writer.print("; ");
writer.breakOp();
cond.dumpExpression(TabbedPrintWriter.IMPL_PAREN, writer);
writer.print("; ");
writer.breakOp();
incrInstr.dumpExpression(TabbedPrintWriter.NO_PAREN, writer);
writer.endOp();
writer.print(")");
break;
}
if (needBrace)
writer.openBrace();
else
writer.println();
writer.tab();
bodyBlock.dumpSource(writer);
writer.untab();
if (type == DOWHILE) {
if (needBrace)
writer.closeBraceContinue();
writer.print("while (");
cond.dumpExpression(TabbedPrintWriter.EXPL_PAREN, writer);
writer.println(");");
} else if (needBrace)
writer.closeBrace();
}
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;
}
/**
* 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) {
if (type == DOWHILE) {
VariableStack afterBody = bodyBlock.mapStackToLocal(stack);
if (afterBody != null)
mergeContinueStack(afterBody);
if (continueStack != null) {
VariableStack newStack;
int params = cond.getFreeOperandCount();
if (params > 0) {
condStack = continueStack.peek(params);
newStack = continueStack.pop(params);
} else
newStack = continueStack;
if (cond != TRUE)
mergeBreakedStack(newStack);
if (cond != FALSE)
stack.merge(newStack);
}
} else {
continueStack = stack;
VariableStack newStack;
int params = cond.getFreeOperandCount();
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);
}
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) {
if (continueStack == null)
continueStack = stack;
else
continueStack.merge(stack);
}
/**
* 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)
cond = condStack.mergeIntoExpression(cond);
bodyBlock.removePush();
}
/**
* 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) {
if (initInstr != null)
initInstr.removeOnetimeLocals();
incrInstr.removeOnetimeLocals();
}
super.removeOnetimeLocals();
}
/**
* 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 :-).
*/
public void replaceBreakContinue(BreakableBlock block) {
java.util.Stack todo = new java.util.Stack();
todo.push(block);
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];
if (breakblk.breaksBlock == block) {
new ContinueBlock(this, breakblk.label != null)
.replace(breakblk);
}
}
todo.push(subs[i]);
}
}
}
/**
* 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;
}
public void simplify() {
cond = cond.simplify();
if (type == FOR) {
incrInstr = incrInstr.simplify();
if (initInstr != null)
initInstr = initInstr.simplify();
}
super.simplify();
}
public boolean doTransformations() {
return ((initBlock == null && type == POSSFOR)
|| (initInstr == null && type == FOR))
&& CreateForInitializer.transform(this, flowBlock.lastModified);
}
}