/* 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 (not 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.
* * 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