/* StructuredBlock 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.GlobalOptions; import net.sf.jode.decompiler.TabbedPrintWriter; import net.sf.jode.decompiler.LocalInfo; import net.sf.jode.decompiler.Declarable; import net.sf.jode.decompiler.ClassAnalyzer; import net.sf.jode.util.SimpleSet; ///#def COLLECTIONS java.util import java.util.Collections; import java.util.Iterator; import java.util.Set; ///#enddef /** * A structured block is the building block of the source programm. * For every program construct like if, while, try, or blocks there is * a corresponding structured block. * * Some of these Block are only intermediate representation, that get * converted to another block later. * * Every block has to handle the local variables that it contains. * This is done by the in/out vectors and the local variable structure * themself. Every local variable used in this structured block is * either in or out. * * There are following types of structured blocks: *
* It will also move the definitions of sb and childs to this block,
* but only descend to sub and not further. It is assumed that
* sub will become a sub block of this block.
* @param sb The structured block that should be replaced.
* @param sub The uppermost sub block of structured block,
* that will be moved to this block (may be this).
*/
public void replace(StructuredBlock sb) {
outer = sb.outer;
setFlowBlock(sb.flowBlock);
if (outer != null) {
outer.replaceSubBlock(sb, this);
} else {
flowBlock.block = this;
}
}
/**
* This function swaps the jump with another block.
* @param block The block whose jump is swapped.
*/
public void swapJump(StructuredBlock block) {
Jump tmp = block.jump;
block.jump = jump;
jump = tmp;
jump.prev = this;
block.jump.prev = block;
}
/**
* This function moves the jump to this block.
* The jump field of the previous owner is cleared afterwards.
* If the given jump is null, nothing bad happens.
* @param jump The jump that should be moved, may be null.
*/
public void moveJump(Jump jump) {
if (this.jump != null)
throw new InternalError("overriding with moveJump()");
this.jump = jump;
if (jump != null) {
jump.prev.jump = null;
jump.prev = this;
}
}
/**
* This function copies the jump to this block.
* If the given jump is null, nothing bad happens.
* @param jump The jump that should be moved, may be null.
*/
public void copyJump(Jump jump) {
if (this.jump != null)
throw new InternalError("overriding with moveJump()");
if (jump != null) {
this.jump = new Jump(jump);
this.jump.prev = this;
}
}
/**
* Appends a block to this block.
* @return the new combined block.
*/
public StructuredBlock appendBlock(StructuredBlock block) {
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;
}
}
/**
* Prepends a block to this block.
* @return the new combined block.
*/
public StructuredBlock prependBlock(StructuredBlock block) {
SequentialBlock sequBlock = new SequentialBlock();
sequBlock.replace(this);
sequBlock.setFirst(block);
sequBlock.setSecond(this);
return sequBlock;
}
/**
* Removes this block, or replaces it with an EmptyBlock.
*/
public final void removeBlock() {
if (outer instanceof SequentialBlock) {
if (outer.getSubBlocks()[1] == this) {
if (jump != null)
outer.getSubBlocks()[0].moveJump(jump);
outer.getSubBlocks()[0].replace(outer);
} else {
if (outer.jump != null)
outer.getSubBlocks()[1].moveJump(outer.jump);
outer.getSubBlocks()[1].replace(outer);
}
return;
}
EmptyBlock eb = new EmptyBlock();
eb.moveJump(jump);
eb.replace(this);
}
/**
* Determines if there is a path, that flows through the end
* of this block. If there is such a path, it is forbidden to
* change the control flow in after this block and this method
* returns false.
* @return true, if the jump may be safely changed.
*/
public boolean flowMayBeChanged() {
return jump != null || jumpMayBeChanged();
}
public boolean jumpMayBeChanged() {
return false;
}
public Set getDeclarables() {
return Collections.EMPTY_SET;
}
/**
* Propagate the used set. Initially the used block contains the
* local that are used in some expression directly in this block.
* This will extend the set, so that a variable is used if it is
* used in at least two sub blocks.
*
* @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();
used.addAll(getDeclarables());
StructuredBlock[] subs = getSubBlocks();
Set allUse = new SimpleSet();
allUse.addAll(used);
for (int i=0; i
*
* @param initialStack the stackmap at begin of the block
* @return the stack after the block has executed.
* @throw RuntimeException if something did get wrong.
*/
public VariableStack mapStackToLocal(VariableStack stack) {
StructuredBlock[] subBlocks = getSubBlocks();
VariableStack after;
if (subBlocks.length == 0)
after = stack;
else {
after = null;
for (int i=0; i< subBlocks.length; i++) {
after = VariableStack.merge
(after, subBlocks[i].mapStackToLocal(stack));
}
}
if (jump != null) {
/* assert(after != null) */
jump.stackMap = after;
return null;
}
return after;
}
/**
* This is called after mapStackToLocal to do the stack to local
* transformation.
*/
public void removePush() {
StructuredBlock[] subBlocks = getSubBlocks();
for (int i=0; i< subBlocks.length; i++)
subBlocks[i].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() {
StructuredBlock[] subBlocks = getSubBlocks();
for (int i=0; i< subBlocks.length; i++)
subBlocks[i].removeOnetimeLocals();
}
/**
* 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.
*
* This will now also combine locals, that use the same slot, have
* compatible types and are declared in the same block.
*
* @param done The set of the already declare variables.
*/
public void makeDeclaration(Set done) {
this.done = new SimpleSet();
this.done.addAll(done);
declare = new SimpleSet();
Iterator iter = used.iterator();
next_used:
while (iter.hasNext()) {
Declarable declarable = (Declarable) iter.next();
// Check if this is already declared.
if (done.contains(declarable))
continue next_used;
if (declarable instanceof LocalInfo) {
LocalInfo local = (LocalInfo) declarable;
/* First generate the names for the locals, since this may
* also change their types, if they are in the local
* variable table.
*/
String localName = local.guessName();
// Merge with all locals in this block, that use the same
// slot and have compatible types and names.
Iterator doneIter = done.iterator();
while (doneIter.hasNext()) {
Declarable previous = (Declarable) doneIter.next();
if (!(previous instanceof LocalInfo))
continue;
LocalInfo prevLocal = (LocalInfo) previous;
/* We only merge locals living in the same
* method and having the same slot.
*
* We don't want to merge variables, whose names
* are not generated by us and differ. And we
* don't want to merge special locals that have a
* constant expression, e.g. this.
*/
if (prevLocal.getMethodAnalyzer()
== local.getMethodAnalyzer()
&& prevLocal.getSlot() == local.getSlot()
&& prevLocal.getType().isOfType(local.getType())
&& (prevLocal.isNameGenerated()
|| local.isNameGenerated()
|| localName.equals(prevLocal.getName()))
&& !prevLocal.isFinal()
&& !local.isFinal()
&& prevLocal.getExpression() == null
&& local.getExpression() == null) {
local.combineWith(prevLocal);
continue next_used;
}
}
}
if (declarable.getName() != null) {
Iterator doneIter = done.iterator();
while (doneIter.hasNext()) {
Declarable previous = (Declarable) doneIter.next();
if (declarable.getName().equals(previous.getName())) {
/* A name conflict happened. */
declarable.makeNameUnique();
break;
}
}
}
done.add(declarable);
declare.add(declarable);
if (declarable instanceof ClassAnalyzer)
((ClassAnalyzer) declarable).makeDeclaration(done);
}
StructuredBlock[] subs = getSubBlocks();
for (int i=0; i