/* SequentialBlock 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.expr.LocalStoreOperator; import net.sf.jode.expr.StoreInstruction; import net.sf.jode.util.SimpleSet; ///#def COLLECTIONS java.util import java.util.Set; ///#enddef /** * A sequential block combines exactly two structured blocks to a new * one. The first sub block mustn't be another sequential block, * instead the second sub block should be used for this. */ public class SequentialBlock extends StructuredBlock { StructuredBlock[] subBlocks; public SequentialBlock() { subBlocks = new StructuredBlock[2]; } public void setFirst(StructuredBlock sb) { subBlocks[0] = sb; sb.outer = this; sb.setFlowBlock(flowBlock); } public void setSecond(StructuredBlock sb) { subBlocks[1] = sb; sb.outer = this; sb.setFlowBlock(flowBlock); } public void checkConsistent() { super.checkConsistent(); if (subBlocks[0].jump != null || subBlocks[0] instanceof SequentialBlock || jump != null) throw new InternalError("Inconsistency"); } /** * This does take the instr into account and modifies stack * accordingly. It then calls super.mapStackToLocal. * @param stack the stack before the instruction is called * @return stack the stack afterwards. */ public VariableStack mapStackToLocal(VariableStack stack) { if (stack == null) net.sf.jode.GlobalOptions.err.println("map stack to local called with null: " + this+ " in "+this.flowBlock); VariableStack middle = subBlocks[0].mapStackToLocal(stack); if (middle != null) // Otherwise the second block is at least "logical" dead code return subBlocks[1].mapStackToLocal(middle); net.sf.jode.GlobalOptions.err.println("Dead code after Block " + subBlocks[0]); return null; } /** * 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() { StructuredBlock secondBlock = subBlocks[1]; if (secondBlock instanceof SequentialBlock) secondBlock = ((SequentialBlock)secondBlock).subBlocks[0]; if (subBlocks[0] instanceof InstructionBlock && secondBlock instanceof InstructionContainer) { InstructionBlock first = (InstructionBlock) subBlocks[0]; InstructionContainer second = (InstructionContainer) secondBlock; /* check if subBlocks[0] writes to a local, second reads * that local, the local is only used by this two blocks, * and there are no side effects. In that case replace * the LoadLocal with the righthandside of subBlocks[0] * and replace subBlocks[1] with this block. Call * removeOnetimelLocals on subBlocks[1] afterwards and * return. */ if (first.getInstruction() instanceof StoreInstruction) { StoreInstruction store = (StoreInstruction) first.getInstruction(); if (store.getLValue() instanceof LocalStoreOperator && (((LocalStoreOperator) store.getLValue()) .getLocalInfo().getUseCount() == 2) && (second.getInstruction().canCombine(store) > 0)) { System.err.println("before: "+first+second); second.setInstruction(second.getInstruction() .combine(store)); System.err.println("after: "+second); StructuredBlock sb = subBlocks[1]; sb.moveDefinitions(this, sb); sb.replace(this); sb.removeOnetimeLocals(); return; } } } super.removeOnetimeLocals(); } /** * 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) { if (subBlock == subBlocks[0]) { if (subBlocks[1].isEmpty()) return subBlocks[1].getNextBlock(); else return subBlocks[1]; } return getNextBlock(); } public FlowBlock getNextFlowBlock(StructuredBlock subBlock) { if (subBlock == subBlocks[0]) { if (subBlocks[1].isEmpty()) return subBlocks[1].getNextFlowBlock(); else return null; } return getNextFlowBlock(); } /** * Tells if the sub block is the single exit point of the current block. * @param subBlock the sub block. * @return true, if the sub block is the single exit point of the * current block. */ public boolean isSingleExit(StructuredBlock subBlock) { return (subBlock == subBlocks[1]); } /** * Propagate the used set. Sequential blocks are special, because * they "use" everything the first block uses. This is, because * the first block can't declare something that is only visible in * the first block. * * @return all locals that are used in this block or in some sub * block (this is not the used set). */ public Set propagateUsage() { used = new SimpleSet(); Set allUse = new SimpleSet(); Set childUse0 = subBlocks[0].propagateUsage(); Set childUse1 = subBlocks[1].propagateUsage(); /* All variables used somewhere inside both sub blocks, are * used in this block, too. * Also the variables used in first block are used in this * block, except when it can be declared locally. (Note that * subBlocks[0].used != childUse0) */ used.addAll(subBlocks[0].used); if (subBlocks[0] instanceof LoopBlock) ((LoopBlock) subBlocks[0]).removeLocallyDeclareable(used); allUse.addAll(childUse0); allUse.addAll(childUse1); childUse0.retainAll(childUse1); used.addAll(childUse0); return allUse; } /** * 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) { super.makeDeclaration(done); if (subBlocks[0] instanceof InstructionBlock) /* An instruction block may declare a variable for us. */ ((InstructionBlock) subBlocks[0]).checkDeclaration(this.declare); } public void dumpInstruction(TabbedPrintWriter writer) throws java.io.IOException { subBlocks[0].dumpSource(writer); subBlocks[1].dumpSource(writer); } /** * 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) { for (int i=0; i<2; i++) { if (subBlocks[i] == oldBlock) { subBlocks[i] = newBlock; return true; } } return false; } /** * Returns all sub block of this structured block. */ public StructuredBlock[] getSubBlocks() { return subBlocks; } /** * 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 (subBlocks[1].jump != null || subBlocks[1].jumpMayBeChanged()); } }