From dff23235cc32467a43c1b6023052f24272a98d59 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 27 Oct 1998 22:33:10 +0000 Subject: [PATCH] Try catch blocks clean up git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@78 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/decompiler/CodeAnalyzer.java | 76 +- jode/jode/flow/CatchBlock.java | 80 +- jode/jode/flow/CombineIfGotoExpressions.java | 3 +- jode/jode/flow/CompleteSynchronized.java | 6 +- jode/jode/flow/CreateAssignExpression.java | 9 +- jode/jode/flow/CreateConstantArray.java | 7 +- jode/jode/flow/CreateExpression.java | 3 +- jode/jode/flow/CreateForInitializer.java | 3 +- jode/jode/flow/CreateIfThenElseOperator.java | 9 +- jode/jode/flow/CreateNewConstructor.java | 6 +- .../jode/flow/CreatePrePostIncExpression.java | 6 +- jode/jode/flow/EmptyBlock.java | 5 +- jode/jode/flow/FlowBlock.java | 587 +------------ jode/jode/flow/InstructionContainer.java | 3 +- jode/jode/flow/LoopBlock.java | 2 +- jode/jode/flow/RemoveEmpty.java | 6 +- jode/jode/flow/RetBlock.java | 3 +- jode/jode/flow/StructuredBlock.java | 24 +- .../jode/flow/TransformExceptionHandlers.java | 812 ++++++++++++++++++ jode/jode/flow/TryBlock.java | 103 +++ 20 files changed, 1053 insertions(+), 700 deletions(-) create mode 100644 jode/jode/flow/TransformExceptionHandlers.java create mode 100644 jode/jode/flow/TryBlock.java diff --git a/jode/jode/decompiler/CodeAnalyzer.java b/jode/jode/decompiler/CodeAnalyzer.java index 0ced5c7..cb6622f 100644 --- a/jode/jode/decompiler/CodeAnalyzer.java +++ b/jode/jode/decompiler/CodeAnalyzer.java @@ -19,9 +19,7 @@ package jode; import jode.flow.FlowBlock; -import jode.flow.Jump; -import jode.flow.StructuredBlock; -import jode.flow.RawTryCatchBlock; +import jode.flow.TransformExceptionHandlers; import java.util.Stack; import java.util.Vector; @@ -37,7 +35,9 @@ import gnu.bytecode.CpoolClass; public class CodeAnalyzer implements Analyzer { + TransformExceptionHandlers handler; FlowBlock methodHeader; + CodeAttr code; MethodAnalyzer method; public JodeEnvironment env; @@ -51,6 +51,19 @@ public class CodeAnalyzer implements Analyzer { */ public MethodAnalyzer getMethod() {return method;} + public CodeAnalyzer(MethodAnalyzer ma, CodeAttr bc, JodeEnvironment e) + throws ClassFormatError + { + code = bc; + method = ma; + env = e; + + int paramCount = method.getParamCount(); + param = new jode.flow.VariableSet(); + for (int i=0; i addr) - ? addr+length : start; - int newEnd = (succ.addr > addr) - ? end : addr; - if (succ.analyzeCatchBlock(newStart, newEnd)) { - break; - } - } if ((succ.addr == addr+length || succ.addr+succ.length == addr) /* Only do T1 transformation if the blocks are @@ -1065,517 +1054,6 @@ public class FlowBlock { } } } - - public void removeJSR(FlowBlock subRoutine) { - Stack jumps = (Stack)successors.remove(subRoutine); - if (jumps == null) - return; - Enumeration enum = jumps.elements(); - while (enum.hasMoreElements()) { - Jump jump = (Jump)enum.nextElement(); - - StructuredBlock prev = jump.prev; - prev.removeJump(); - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock) { - if (prev.outer.getNextFlowBlock() != null) { - /* The jsr is directly before a jump, okay. */ - prev.outer.removeBlock(); - continue; - } - if (prev.outer.outer instanceof SequentialBlock - && prev.outer.outer.getSubBlocks()[0] == prev.outer) { - SequentialBlock seq = (SequentialBlock) prev.outer.outer; - if (seq.subBlocks[1] instanceof JsrBlock) { - /* The jsr is followed by a jsr, okay. */ - prev.outer.removeBlock(); - continue; - } - if (seq.subBlocks[1] instanceof ReturnBlock - && !(seq.subBlocks[1] instanceof ThrowBlock)) { - - /* The jsr is followed by a return, okay. */ - ReturnBlock ret = (ReturnBlock) seq.subBlocks[1]; - prev.outer.removeBlock(); - - if (ret.outer != null - && ret.outer instanceof SequentialBlock) { - /* Try to eliminate the local that javac uses - * in this case. - */ - try { - ComplexExpression expr = (ComplexExpression) - ((InstructionBlock) - ret.outer.getSubBlocks()[0]).instr; - LocalStoreOperator store = - (LocalStoreOperator) expr.getOperator(); - if (store.matches((LocalLoadOperator) - ret.getInstruction())) { - ret.setInstruction(expr. - getSubExpressions()[0]); - ret.replace(ret.outer, null); - ret.used.removeElement - (store.getLocalInfo()); - } - } catch(ClassCastException ex) { - /* didn't succeed */ - } - } - continue; - } - } - } - /* Now we have a dangling JSR at the wrong place. - * We don't do anything, so that JSR will show up in - * the output. - */ - } - } - - public void checkAndRemoveJSR(FlowBlock subRoutine) { - Enumeration keys = successors.keys(); - Enumeration stacks = successors.elements(); - while (keys.hasMoreElements()) { - Stack jumps = (Stack) stacks.nextElement(); - if (keys.nextElement() == subRoutine) - continue; - - Enumeration enum = jumps.elements(); - while (enum.hasMoreElements()) { - Jump jump = (Jump)enum.nextElement(); - - StructuredBlock prev = jump.prev; - if (prev instanceof ThrowBlock) { - /* The jump is a throw. We have a catch-all block - * that will do the finally. - */ - continue; - } - if (prev instanceof JsrBlock) { - /* The jump is directly preceeded by a jsr. - * Everything okay. - */ - continue; - } - - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock) { - /* If jump is a jsr check the outer - * block instead. - */ - prev = prev.outer; - } - if ((prev instanceof ReturnBlock - || prev instanceof JsrBlock) - && prev.outer instanceof SequentialBlock) { - SequentialBlock seq = (SequentialBlock) prev.outer; - if (seq.subBlocks[1] == prev - && (seq.subBlocks[0] instanceof JsrBlock)) { - /* The jump is preceeded by another jsr, okay. - */ - continue; - } - if (seq.subBlocks[0] == prev - && seq.outer instanceof SequentialBlock - && (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) { - /* Again the jump is preceeded by another jsr, okay. - */ - continue; - } - } - /* Now we have a jump with a wrong destination. - * Complain! - */ - System.err.println("non well formed try-finally block"); - } - } - removeJSR(subRoutine); - } - - public boolean isMonitorExit(Expression instr, LocalInfo local) { - if (instr instanceof ComplexExpression) { - ComplexExpression expr = (ComplexExpression)instr; - if (expr.getOperator() instanceof MonitorExitOperator - && expr.getSubExpressions()[0] instanceof LocalLoadOperator - && (((LocalLoadOperator) expr.getSubExpressions()[0]) - .getLocalInfo().getSlot() == local.getSlot())) { - return true; - } - } - return false; - } - - public void checkAndRemoveMonitorExit(LocalInfo local, int end) { - FlowBlock subRoutine = null; - Enumeration stacks = successors.elements(); - dest_loop: - while (stacks.hasMoreElements()) { - Enumeration enum = ((Stack) stacks.nextElement()).elements(); - while (enum.hasMoreElements()) { - Jump jump = (Jump)enum.nextElement(); - - StructuredBlock prev = jump.prev; - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock - && subRoutine == null) { - - subRoutine = jump.destination; - subRoutine.analyzeSubRoutine(addr+length, end); - - if (subRoutine.block instanceof InstructionBlock) { - Expression instr = - ((InstructionBlock)subRoutine.block) - .getInstruction(); - if (isMonitorExit(instr, local)) { - - updateInOut(subRoutine, true, - (Stack)successors.get(subRoutine)); - length += subRoutine.length; - continue dest_loop; - } - } - } - - if (prev instanceof ThrowBlock) { - /* The jump is a throw. We have a catch all block - * that will do the monitorexit. - */ - continue; - } - if (prev instanceof JsrBlock) { - /* The jump is directly preceeded by a jsr. - */ - continue; - } - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock) { - /* If jump is a jsr check the outer - * block instead. - */ - prev = prev.outer; - } - - if ((prev instanceof JsrBlock - || prev instanceof ReturnBlock) - && prev.outer instanceof SequentialBlock) { - SequentialBlock seq = (SequentialBlock) prev.outer; - StructuredBlock pred = null; - if (seq.subBlocks[1] == prev) - pred = seq.subBlocks[0]; - else if (seq.outer instanceof SequentialBlock) - pred = seq.outer.getSubBlocks()[0]; - - if (pred != null) { - if (pred instanceof JsrBlock || pred.jump != null) - /* The jump is preceeded by another jump - * or jsr and last in its block, okay. - */ - continue; - - if (pred instanceof InstructionBlock) { - Expression instr = - ((InstructionBlock)pred).getInstruction(); - if (instr instanceof ComplexExpression - && ((ComplexExpression)instr) - .getOperator() instanceof MonitorExitOperator - && ((ComplexExpression)instr) - .getSubExpressions()[0] - instanceof LocalLoadOperator - && (((LocalLoadOperator) - ((ComplexExpression)instr) - .getSubExpressions()[0]).getLocalInfo() - .getSlot() == local.getSlot())) - continue; - } - } - } - - if (prev instanceof InstructionBlock - && isMonitorExit(((InstructionBlock)prev).instr, local)) { - /* This is probably the last expression in the - * synchronized block, and has the right monitor exit - * attached. Remove this block. - */ - prev.removeBlock(); - continue; - } - - /* Now we have a jump that is not preceded by a monitorexit. - * Complain! - */ - System.err.println("non well formed synchronized block"); - } - } - - if (subRoutine != null) - removeJSR(subRoutine); - } - - - /** - * Create a Catch- resp. FinallyBlock (maybe even SynchronizedBlock). - * The root block MUST be a RawTryCatchBlock. - * @param start the start address - * @param end and the end address of FlowBlocks, we may use. - */ - public boolean analyzeCatchBlock(int start, int end) { - if (Decompiler.debugAnalyze) - System.err.println("analyzeCatch("+start+", "+end+")"); - RawTryCatchBlock rawBlock = (RawTryCatchBlock) block; - FlowBlock tryFlow = rawBlock.tryBlock.jump.destination; - FlowBlock catchFlow = rawBlock.catchBlock.jump.destination; - - /* This are the only jumps in this block. Make sure they - * are disjunct, the successing code relies on that! - */ - if (tryFlow == catchFlow) - throw new AssertError("try == catch"); - - checkConsistent(); - boolean changed = false; - while(tryFlow.analyze(addr, catchFlow.addr)); - while(catchFlow.analyze(catchFlow.addr, end)); - checkConsistent(); - - updateInOut(tryFlow, true, (Stack) successors.remove(tryFlow)); - updateInOut(catchFlow, true, (Stack) successors.remove(catchFlow)); - length += tryFlow.length; - length += catchFlow.length; - - if (rawBlock.type != null) { - /* simple try catch block: - * - * try-header - * |- first instruction - * | ... - * | last instruction - * |- optional jump (last+1) - * | ... - * `- catch block - */ - CatchBlock newBlock = new CatchBlock(rawBlock.type); - newBlock.replace(rawBlock, rawBlock); - newBlock.moveJump(rawBlock.jump); - - newBlock.setTryBlock(tryFlow.block); - mergeSuccessors(tryFlow); - newBlock.setCatchBlock(catchFlow.block); - mergeSuccessors(catchFlow); - - lastModified = newBlock; - changed = true; - } else if (catchFlow.block instanceof SequentialBlock - && catchFlow.block.getSubBlocks()[0] - instanceof InstructionBlock) { - - SequentialBlock catchBlock = (SequentialBlock) catchFlow.block; - int type = 0; - - Expression instr = - ((InstructionBlock)catchBlock.subBlocks[0]).getInstruction(); - - if (instr instanceof ComplexExpression - && ((ComplexExpression)instr).getOperator() - instanceof MonitorExitOperator - && ((ComplexExpression)instr).getSubExpressions()[0] - instanceof LocalLoadOperator - && catchBlock.subBlocks[1] instanceof ThrowBlock - && ((ThrowBlock)catchBlock.subBlocks[1]).instr - instanceof NopOperator) { - - /* This is a synchronized block: - * - * local_x = monitor object; // later - * monitorenter local_x // later - * try-header any - * |- syncronized block - * | ... - * | every jump to outside is preceded by jsr monexit ---, - * | ... | - * |- monitorexit local_x | - * | jump after this block (without jsr monexit) | - * `- catch any | - * local_n = stack | - * monitorexit local_x | - * throw local_n | - * monexit: <-----------------------------------------------' - * astore_n - * monitorexit local_x - * return_n - */ - - /* Now remove the jump (after the throw) from the - * catch block so that we can forget about it. - */ - catchBlock.subBlocks[1] - .jump.destination.predecessors.removeElement(catchFlow); - - ComplexExpression monexit = (ComplexExpression) - ((InstructionBlock) catchBlock.subBlocks[0]).instr; - LocalInfo local = - ((LocalLoadOperator)monexit.getSubExpressions()[0]) - .getLocalInfo(); - - length -= tryFlow.length; - tryFlow.checkAndRemoveMonitorExit(local, end); - length += tryFlow.length; - - SynchronizedBlock syncBlock = new SynchronizedBlock(local); - syncBlock.replace(rawBlock, rawBlock); - syncBlock.moveJump(rawBlock.jump); - syncBlock.setBodyBlock(tryFlow.block); - mergeSuccessors(tryFlow); - lastModified = syncBlock; - changed = true; - - } else if (catchBlock.subBlocks[1] instanceof SequentialBlock - && catchBlock.subBlocks[1].getSubBlocks()[0] - instanceof JsrBlock - && instr instanceof LocalStoreOperator - && catchBlock.subBlocks[1].getSubBlocks()[1] - instanceof ThrowBlock - && ((ThrowBlock)catchBlock.subBlocks[1] - .getSubBlocks()[1]).instr - instanceof LocalLoadOperator - && ((LocalStoreOperator) instr) - .matches((LocalLoadOperator) - ((ThrowBlock)catchBlock.subBlocks[1] - .getSubBlocks()[1]).instr)) { - /* Wow that was complicated :-) - * But now we know that the catch block looks - * exactly like an try finally block: - * - * try-header any - * | - * |- first instruction - * | ... - * | every jump to outside is preceded by jsr finally - * | ... - * | jsr finally -----------------, - * |- jump after finally | - * `- catch any | - * local_n = stack v - * jsr finally ---------------->| - * throw local_n; | - * finally: <-----------------------' - * astore_n - * ... - * return_n - */ - FlowBlock subRoutine = - ((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0]) - .innerBlock.jump.destination; - - /* Now remove the two jumps of the catch block - * so that we can forget about them. - * This are the jsr and the throw. - */ - catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0] - .jump.destination.predecessors.removeElement(catchFlow); - catchBlock.subBlocks[1].getSubBlocks()[1] - .jump.destination.predecessors.removeElement(catchFlow); - - subRoutine.analyzeSubRoutine(addr+length, end); - Stack jumps = (Stack)successors.get(subRoutine); - if (jumps != null) - updateInOut(subRoutine, true, jumps); - - if (subRoutine.successors.size() != 0) - throw new AssertError("Jump inside subroutine"); - length += subRoutine.length; - tryFlow.checkAndRemoveJSR(subRoutine); - - CatchFinallyBlock newBlock = new CatchFinallyBlock(); - newBlock.replace(rawBlock, rawBlock); - newBlock.setTryBlock(tryFlow.block); - mergeSuccessors(tryFlow); - newBlock.setFinallyBlock(subRoutine.block); - mergeSuccessors(subRoutine); - newBlock.moveJump(rawBlock.jump); - lastModified = newBlock; - changed = true; - } - } else if (catchFlow.block instanceof InstructionBlock - && ((InstructionBlock) catchFlow.block).getInstruction() - instanceof PopOperator - && ((PopOperator) ((InstructionBlock) catchFlow.block) - .getInstruction()).getCount() == 1) { - - /* This is a special try/finally-block, where - * the finally block ends with a break, return or - * similar. - */ - FlowBlock succ = catchFlow.block.jump.destination; - Stack jumps = (Stack) tryFlow.successors.remove(succ); - if (tryFlow.successors.size() > 0) { - /* Only do the rest if tryFlow has no other exit point, - * undo the previous remove. - */ - tryFlow.successors.put(succ,jumps); - - } else { - - succ.predecessors.removeElement(tryFlow); - - /* Handle the jumps in the tryFlow. Note that - * we call updateInOut on ourself, don't change it. - */ - updateInOut(succ, true, jumps); - tryFlow.optimizeJumps(jumps, succ); - tryFlow.resolveRemaining(jumps); - - CatchFinallyBlock newBlock = new CatchFinallyBlock(); - newBlock.replace(rawBlock, rawBlock); - newBlock.setTryBlock(tryFlow.block); - /* try block has no successors */ - - if (succ.predecessors.size() == 1) { - while (succ.analyze(addr+length, end)); - length += succ.length; - successors.remove(succ); - newBlock.setFinallyBlock(succ.block); - mergeSuccessors(succ); - } else { - /* The finally block is empty, put the jump back - * into the finally block. - */ - newBlock.setFinallyBlock - (new EmptyBlock(catchFlow.block.jump)); - mergeSuccessors(catchFlow); - } - lastModified = newBlock; - changed = true; - } - } - checkConsistent(); - if (Decompiler.debugAnalyze) - System.err.println("analyzeCatch("+start+", "+end+") " - +(changed?"succeeded":"failed") - +"; "+addr+","+(addr+length)); - return changed; - } - - public void analyzeSubRoutine(int start, int end) { - analyze(start, end); - /* throws ClassCastException if something isn't as exspected. */ - SequentialBlock sequBlock = (SequentialBlock) block; - LocalStoreOperator store = (LocalStoreOperator) - ((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator(); - - while (sequBlock.subBlocks[1] instanceof SequentialBlock) - sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; - - if (! ((RetBlock)sequBlock.subBlocks[1]).local - .equals(store.getLocalInfo())) - throw new AssertError("Ret doesn't match"); - - if (sequBlock.outer == null) { - new EmptyBlock().replace(sequBlock, sequBlock); - } else { - sequBlock.subBlocks[0].replace(sequBlock, sequBlock); - block.getSubBlocks()[1].replace(block, block); - } - } /** * The switch analyzation. This calls doSwitchT1 and doT2 on apropriate @@ -1646,7 +1124,7 @@ public class FlowBlock { if (lastFlow != null) { lastFlow.block.replace - (switchBlock.caseBlocks[last].subBlock, null); + (switchBlock.caseBlocks[last].subBlock); mergeSuccessors(lastFlow); } @@ -1667,7 +1145,7 @@ public class FlowBlock { } if (lastFlow != null) { lastFlow.block.replace - (switchBlock.caseBlocks[last].subBlock, null); + (switchBlock.caseBlocks[last].subBlock); mergeSuccessors(lastFlow); } checkConsistent(); @@ -1679,10 +1157,6 @@ public class FlowBlock { * the successors map. */ public void resolveJumps(FlowBlock[] instr) { - if (block instanceof RawTryCatchBlock) { - ((RawTryCatchBlock)block).getTryBlock() - .jump.destination.resolveJumps(instr); - } Vector allJumps = new Vector(); block.fillSuccessors(allJumps); Enumeration enum = allJumps.elements(); @@ -1739,8 +1213,7 @@ public class FlowBlock { public void dumpSource(TabbedPrintWriter writer) throws java.io.IOException { - if (predecessors.size() != 1 || - predecessors.elementAt(0) != null) { + if (predecessors.size() != 0) { writer.untab(); writer.println(label+":"); writer.tab(); diff --git a/jode/jode/flow/InstructionContainer.java b/jode/jode/flow/InstructionContainer.java index 2e124ec..1e77d0b 100644 --- a/jode/jode/flow/InstructionContainer.java +++ b/jode/jode/flow/InstructionContainer.java @@ -46,12 +46,13 @@ public abstract class InstructionContainer extends StructuredBlock { * Fill all in variables into the given VariableSet. * @param in The VariableSet, the in variables should be stored to. */ - public void fillInSet(VariableSet in) { + public void fillInGenSet(VariableSet in, VariableSet gen) { if (instr instanceof LocalVarOperator) { LocalVarOperator varOp = (LocalVarOperator) instr; if (varOp.isRead()) { in.addElement(varOp.getLocalInfo()); } + gen.addElement(varOp.getLocalInfo()); } } diff --git a/jode/jode/flow/LoopBlock.java b/jode/jode/flow/LoopBlock.java index ebdcd80..29755b2 100644 --- a/jode/jode/flow/LoopBlock.java +++ b/jode/jode/flow/LoopBlock.java @@ -237,7 +237,7 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { BreakBlock breakblk = (BreakBlock) subs[i]; if (breakblk.breaksBlock == block) { new ContinueBlock(this, breakblk.label != null) - .replace(breakblk, null); + .replace(breakblk); } } todo.push(subs[i]); diff --git a/jode/jode/flow/RemoveEmpty.java b/jode/jode/flow/RemoveEmpty.java index 08c27bf..6f700e9 100644 --- a/jode/jode/flow/RemoveEmpty.java +++ b/jode/jode/flow/RemoveEmpty.java @@ -49,7 +49,7 @@ public class RemoveEmpty implements Transformation { instr.setType(nopInstr.getType()); ic.setInstruction(instr); - ic.replace(ic.outer, ic); + ic.replace(ic.outer); return true; } return false; @@ -62,7 +62,7 @@ public class RemoveEmpty implements Transformation { lastBlock.outer.getSubBlocks()[1] == lastBlock) { StructuredBlock block = lastBlock.outer.getSubBlocks()[0]; - block.replace(block.outer, block); + block.replace(block.outer); if (lastBlock.jump != null) block.moveJump(lastBlock.jump); flow.lastModified = block; @@ -72,7 +72,7 @@ public class RemoveEmpty implements Transformation { lastBlock.outer.getSubBlocks()[0] instanceof EmptyBlock && lastBlock.outer.getSubBlocks()[0].jump == null) { - lastBlock.replace(lastBlock.outer, lastBlock); + lastBlock.replace(lastBlock.outer); flow.lastModified = lastBlock; return true; } diff --git a/jode/jode/flow/RetBlock.java b/jode/jode/flow/RetBlock.java index e9c39c7..01184a2 100644 --- a/jode/jode/flow/RetBlock.java +++ b/jode/jode/flow/RetBlock.java @@ -41,8 +41,9 @@ public class RetBlock extends StructuredBlock { * Fill all in variables into the given VariableSet. * @param in The VariableSet, the in variables should be stored to. */ - public void fillInSet(VariableSet in) { + public void fillInGenSet(VariableSet in, VariableSet gen) { in.addElement(local); + gen.addElement(local); } public void dumpInstruction(jode.TabbedPrintWriter writer) diff --git a/jode/jode/flow/StructuredBlock.java b/jode/jode/flow/StructuredBlock.java index 400ab3c..2fd95c6 100644 --- a/jode/jode/flow/StructuredBlock.java +++ b/jode/jode/flow/StructuredBlock.java @@ -78,9 +78,16 @@ public abstract class StructuredBlock { * block in a flow block, outer is null. */ StructuredBlock outer; +// /** +// * The surrounding non sequential block. This is the same as if +// * you would repeatedly get outer until you reach a non sequential +// * block. This is field is only valid, if the outer block is a +// * sequential block. +// */ +// StructuredBlock realOuter; + /** - * The flow block in which this structured block lies. - */ + * The flow block in which this structured block lies. */ FlowBlock flowBlock; /** @@ -231,8 +238,7 @@ public abstract class StructuredBlock { * @param sub The uppermost sub block of structured block, * that will be moved to this block (may be this). */ - public void replace(StructuredBlock sb, StructuredBlock sub) { - moveDefinitions(sb, sub); + public void replace(StructuredBlock sb) { outer = sb.outer; setFlowBlock(sb.flowBlock); if (outer != null) { @@ -277,7 +283,7 @@ public abstract class StructuredBlock { */ public StructuredBlock appendBlock(StructuredBlock block) { SequentialBlock sequBlock = new SequentialBlock(); - sequBlock.replace(this, this); + sequBlock.replace(this); sequBlock.setFirst(this); sequBlock.setSecond(block); return sequBlock; @@ -292,15 +298,15 @@ public abstract class StructuredBlock { if (outer.getSubBlocks()[1] == this) { if (jump != null) outer.getSubBlocks()[0].moveJump(jump); - outer.getSubBlocks()[0].replace(outer, null); + outer.getSubBlocks()[0].replace(outer); } else - outer.getSubBlocks()[1].replace(outer, null); + outer.getSubBlocks()[1].replace(outer); return; } EmptyBlock eb = new EmptyBlock(); eb.moveJump(jump); - eb.replace(this, null); + eb.replace(this); } /** @@ -398,7 +404,7 @@ public abstract class StructuredBlock { * Fill all in variables into the given VariableSet. * @param in The VariableSet, the in variables should be stored to. */ - public void fillInSet(VariableSet in) { + public void fillInGenSet(VariableSet in, VariableSet gen) { /* overwritten by InstructionContainer */ } diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java new file mode 100644 index 0000000..8dfe6ae --- /dev/null +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -0,0 +1,812 @@ +/* TransformExceptionHandlers Copyright (C) 1997-1998 Jochen Hoenicke. + * + * 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. + * + * 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 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. + * + * $Id$ + */ +package jode.flow; +import jode.AssertError; +import jode.Type; +import jode.LocalInfo; +import jode.ComplexExpression; +import jode.LocalStoreOperator; +import jode.NopOperator; +import jode.MonitorExitOperator; +import jode.LocalLoadOperator; +import jode.Expression; +import jode.PopOperator; +import java.util.Enumeration; +import java.util.Stack; + +/** + * + * @author Jochen Hoenicke + */ +public class TransformExceptionHandlers { + FlowBlock[] flows; + int count; + int[] startPCs = new int[4]; + int[] endPCs = new int[4]; + int[] handlerPCs = new int[4]; + Type[] types = new Type[4]; + + public TransformExceptionHandlers(FlowBlock[] flowBlocks) { + flows = flowBlocks; + count = 0; + } + + /** + * Add an exception Handler. + * @param start The start address of the exception range. + * @param end The end address of the exception range + 1. + * @param handler The address of the handler. + * @param type The type of the exception, null for ALL. + */ + public void addHandler(int start, int end, int handler, Type type) { + int offset = 0; + /* First sort by start offsets, highest address first...*/ + while (offset < count && start < startPCs[offset]) + offset++; + /* ...Second sort by end offsets, lowest address first... + * this will move the innermost blocks to the beginning. */ + while (offset < count && start == startPCs[offset] + && end > endPCs[offset]) + offset++; + /* ...Last sort by handler offsets, lowest first */ + while (offset < count && start == startPCs[offset] + && end == endPCs[offset] && handler > handlerPCs[offset]) + offset++; + + if (count++ >= startPCs.length) { + /* We grow the arrays by 50 % */ + int newSize = startPCs.length * 3 / 2; + int[] newStartPCs = new int[newSize]; + int[] newEndPCs = new int[newSize]; + int[] newHandlerPCs = new int[newSize]; + Type[] newTypes = new Type[newSize]; + System.arraycopy(startPCs, 0, newStartPCs, 0, offset); + System.arraycopy(endPCs, 0, newEndPCs, 0, offset); + System.arraycopy(handlerPCs, 0, newHandlerPCs, 0, offset); + System.arraycopy(types, 0, newTypes, 0, offset); + + if (offset+1 < count) { + System.arraycopy(startPCs, offset, newStartPCs, offset+1, + count-offset-1); + System.arraycopy(endPCs, offset, newEndPCs, offset+1, + count-offset-1); + System.arraycopy(handlerPCs, offset, newHandlerPCs, offset+1, + count-offset-1); + System.arraycopy(types, offset, newTypes, offset+1, + count-offset-1); + } + startPCs = newStartPCs; + endPCs = newEndPCs; + handlerPCs = newHandlerPCs; + types = newTypes; + } else if (offset+1 < count) { + /* Move the tailing data one place below + */ + System.arraycopy(startPCs, offset, startPCs, offset+1, + count-offset-1); + System.arraycopy(endPCs, offset, endPCs, offset+1, + count-offset-1); + System.arraycopy(handlerPCs, offset, handlerPCs, offset+1, + count-offset-1); + System.arraycopy(types, offset, types, offset+1, + count-offset-1); + } + /* Insert the new handler */ + startPCs[offset] = start; + endPCs[offset] = end; + handlerPCs[offset] = handler; + types[offset] = type; + } + + /** + * Updates the in/out-Vectors of the structured block of the + * successing flow block for a try catch block. The main difference + * to updateInOut in FlowBlock is, that this function works, as if + * every instruction would have a jump. This is because every + * instruction can throw an exception and thus enter the catch block. + * + * @param successor The flow block which is unified with this flow + * block. + * @return The variables that must be defined in this block. + */ + static void updateInOutCatch (FlowBlock tryFlow, FlowBlock catchFlow) { + VariableSet gens = ((TryBlock)tryFlow.block).gen; + + /* Merge the locals used in the catch block with those written + * by the try block + */ + catchFlow.in.merge(gens); + + /* The gen/kill sets must be updated for every jump + * in the catch block */ + Enumeration stacks = catchFlow.successors.elements(); + while (stacks.hasMoreElements()) { + Enumeration enum = ((Stack) stacks.nextElement()).elements(); + while (enum.hasMoreElements()) { + + Jump jump = (Jump) enum.nextElement(); + if (jump != null) { + jump.gen.mergeGenKill(gens, jump.kill); + } + } + } + tryFlow.in.unionExact(catchFlow.in); + tryFlow.gen.unionExact(catchFlow.gen); + + if (jode.Decompiler.debugInOut) { + System.err.println("UpdateInOutCatch: gens : "+gens); + System.err.println(" s.in : "+catchFlow.in); + System.err.println(" in : "+tryFlow.in); + } + } + + + /* simple try catch block: + * + * try-header + * |- first instruction + * | ... + * | last instruction + * |- optional jump (last+1) + * | ... + * `- catch block + */ + static int serialno=0; + + static void analyzeCatchBlock(Type type, + FlowBlock tryFlow, FlowBlock catchFlow) { + + StructuredBlock catchBlock = catchFlow.block; + LocalInfo local = null; + StructuredBlock firstInstr = (catchBlock instanceof SequentialBlock) + ? catchBlock.getSubBlocks()[0] : catchBlock; + + if (firstInstr instanceof InstructionBlock) { + Expression instr = + ((InstructionBlock) firstInstr).getInstruction(); + if (instr instanceof PopOperator + && ((PopOperator) instr).getCount() == 1) { + /* The exception is ignored. Create a dummy local for it */ + local = new LocalInfo(-1); + local.setName("exception_"+(serialno++)+"_"); + firstInstr.removeBlock(); + + } else if (instr instanceof jode.LocalStoreOperator) { + /* The exception is stored in a local variable */ + local = + ((jode.LocalStoreOperator) instr).getLocalInfo(); + firstInstr.removeBlock(); + } + } + + if (local == null) { + local = new LocalInfo(-1); + local.setName("ERROR!!!"); + } + local.setType(type); + + CatchBlock newBlock = new CatchBlock(type, local); + ((TryBlock)tryFlow.block).addCatchBlock(newBlock); + newBlock.setCatchBlock(catchFlow.block); + tryFlow.mergeSuccessors(catchFlow); + tryFlow.length += catchFlow.length; + } + + /* And now the complicated parts. */ + + /** + * This transforms a sub routine, that is checks if the beginning + * local assignment matches the final ret and then returns. + */ + boolean transformSubRoutine(FlowBlock subRoutine) { + try { + SequentialBlock sequBlock = (SequentialBlock) subRoutine.block; + LocalStoreOperator store = (LocalStoreOperator) + ((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator(); + while (sequBlock.subBlocks[1] instanceof SequentialBlock) + sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; + RetBlock retBlock = (RetBlock)sequBlock.subBlocks[1]; + if (! retBlock.local.equals(store.getLocalInfo())) + /* Ret doesn't match */ + return false; + subRoutine.block.getSubBlocks()[0].removeBlock(); + retBlock.removeBlock(); + return true; + } catch (ClassCastException ex) { + return false; + } + } + + /** + * Remove the JSR's jumping to the specified subRoutine. It + * is checked if the next block is a leaving instruction, and + * otherwise the JsrBlock is not removed (to give the user a + * hint that something went wrong). This will also remove the + * local javac generates for returns. + * @param tryFlow the FlowBLock of the try block. + * @param subRoutine the FlowBlock of the sub routine. + */ + private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) { + Stack jumps = (Stack)tryFlow.successors.remove(subRoutine); + if (jumps == null) + return; + Enumeration enum = jumps.elements(); + while (enum.hasMoreElements()) { + Jump jump = (Jump)enum.nextElement(); + + StructuredBlock prev = jump.prev; + prev.removeJump(); + if (prev instanceof EmptyBlock + && prev.outer instanceof JsrBlock) { + if (prev.outer.getNextFlowBlock() != null) { + /* The jsr is directly before a jump, okay. */ + prev.outer.removeBlock(); + continue; + } + if (prev.outer.outer instanceof SequentialBlock + && prev.outer.outer.getSubBlocks()[0] == prev.outer) { + SequentialBlock seq = (SequentialBlock) prev.outer.outer; + if (seq.subBlocks[1] instanceof JsrBlock) { + /* The jsr is followed by a jsr, okay. */ + prev.outer.removeBlock(); + continue; + } + if (seq.subBlocks[1] instanceof ReturnBlock + && !(seq.subBlocks[1] instanceof ThrowBlock)) { + + /* The jsr is followed by a return, okay. */ + ReturnBlock ret = (ReturnBlock) seq.subBlocks[1]; + prev.outer.removeBlock(); + + if (ret.outer != null + && ret.outer instanceof SequentialBlock) { + /* Try to eliminate the local that javac uses + * in this case. + */ + try { + ComplexExpression expr = (ComplexExpression) + ((InstructionBlock) + ret.outer.getSubBlocks()[0]).instr; + LocalStoreOperator store = + (LocalStoreOperator) expr.getOperator(); + if (store.matches((LocalLoadOperator) + ret.getInstruction())) { + ret.setInstruction(expr. + getSubExpressions()[0]); + ret.replace(ret.outer); + ret.used.removeElement + (store.getLocalInfo()); + } + } catch(ClassCastException ex) { + /* didn't succeed */ + } + } + continue; + } + } + } + /* Now we have a dangling JSR at the wrong place. + * We don't do anything, so that JSR will show up in + * the output. + */ + } + } + + public void checkAndRemoveJSR(FlowBlock tryFlow, FlowBlock subRoutine) { + Enumeration keys = tryFlow.successors.keys(); + Enumeration stacks = tryFlow.successors.elements(); + while (keys.hasMoreElements()) { + Stack jumps = (Stack) stacks.nextElement(); + if (keys.nextElement() == subRoutine) + continue; + + Enumeration enum = jumps.elements(); + while (enum.hasMoreElements()) { + Jump jump = (Jump)enum.nextElement(); + + StructuredBlock prev = jump.prev; + if (prev instanceof ThrowBlock) { + /* The jump is a throw. We have a catch-all block + * that will do the finally. + */ + continue; + } + if (prev instanceof JsrBlock) { + /* The jump is directly preceeded by a jsr. + * Everything okay. + */ + continue; + } + + if (prev instanceof EmptyBlock + && prev.outer instanceof JsrBlock) { + /* If jump is a jsr check the outer + * block instead. + */ + prev = prev.outer; + } + if ((prev instanceof ReturnBlock + || prev instanceof JsrBlock) + && prev.outer instanceof SequentialBlock) { + SequentialBlock seq = (SequentialBlock) prev.outer; + if (seq.subBlocks[1] == prev + && (seq.subBlocks[0] instanceof JsrBlock)) { + /* The jump is preceeded by another jsr, okay. + */ + continue; + } + if (seq.subBlocks[0] == prev + && seq.outer instanceof SequentialBlock + && (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) { + /* Again the jump is preceeded by another jsr, okay. + */ + continue; + } + } + /* Now we have a jump with a wrong destination. + * Complain! + */ + System.err.println("non well formed try-finally block"); + } + } + removeJSR(tryFlow, subRoutine); + } + + static boolean isMonitorExit(Expression instr, LocalInfo local) { + if (instr instanceof ComplexExpression) { + ComplexExpression expr = (ComplexExpression)instr; + if (expr.getOperator() instanceof MonitorExitOperator + && expr.getSubExpressions()[0] instanceof LocalLoadOperator + && (((LocalLoadOperator) expr.getSubExpressions()[0]) + .getLocalInfo().getSlot() == local.getSlot())) { + return true; + } + } + return false; + } + + public void checkAndRemoveMonitorExit(FlowBlock tryFlow, LocalInfo local, + int startMonExit, int endMonExit) { + FlowBlock subRoutine = null; + Enumeration stacks = tryFlow.successors.elements(); + dest_loop: + while (stacks.hasMoreElements()) { + Enumeration enum = ((Stack) stacks.nextElement()).elements(); + while (enum.hasMoreElements()) { + Jump jump = (Jump)enum.nextElement(); + + StructuredBlock prev = jump.prev; + if (prev instanceof EmptyBlock + && prev.outer instanceof JsrBlock + && subRoutine == null) { + + subRoutine = jump.destination; + subRoutine.analyze(startMonExit, endMonExit); + transformSubRoutine(subRoutine); + + if (subRoutine.block instanceof InstructionBlock) { + Expression instr = + ((InstructionBlock)subRoutine.block) + .getInstruction(); + if (isMonitorExit(instr, local)) { + tryFlow.length += subRoutine.length; + continue dest_loop; + } + } + } + + if (prev instanceof ThrowBlock) { + /* The jump is a throw. We have a catch all block + * that will do the monitorexit. + */ + continue; + } + if (prev instanceof JsrBlock) { + /* The jump is directly preceeded by a jsr. + */ + continue; + } + if (prev instanceof EmptyBlock + && prev.outer instanceof JsrBlock) { + /* If jump is a jsr check the outer + * block instead. + */ + prev = prev.outer; + } + + if ((prev instanceof JsrBlock + || prev instanceof ReturnBlock) + && prev.outer instanceof SequentialBlock) { + SequentialBlock seq = (SequentialBlock) prev.outer; + StructuredBlock pred = null; + if (seq.subBlocks[1] == prev) + pred = seq.subBlocks[0]; + else if (seq.outer instanceof SequentialBlock) + pred = seq.outer.getSubBlocks()[0]; + + if (pred != null) { + if (pred instanceof JsrBlock || pred.jump != null) + /* The jump is preceeded by another jump + * or jsr and last in its block, okay. + */ + continue; + + if (pred instanceof InstructionBlock) { + Expression instr = + ((InstructionBlock)pred).getInstruction(); + if (instr instanceof ComplexExpression + && ((ComplexExpression)instr) + .getOperator() instanceof MonitorExitOperator + && ((ComplexExpression)instr) + .getSubExpressions()[0] + instanceof LocalLoadOperator + && (((LocalLoadOperator) + ((ComplexExpression)instr) + .getSubExpressions()[0]).getLocalInfo() + .getSlot() == local.getSlot())) + continue; + } + } + } + + if (prev instanceof InstructionBlock + && isMonitorExit(((InstructionBlock)prev).instr, local)) { + /* This is probably the last expression in the + * synchronized block, and has the right monitor exit + * attached. Remove this block. + */ + prev.removeBlock(); + continue; + } + + /* Now we have a jump that is not preceded by a monitorexit. + * Complain! + */ + System.err.println("non well formed synchronized block"); + } + } + + if (subRoutine != null) + removeJSR(tryFlow, subRoutine); + } + + private boolean analyzeSynchronized(FlowBlock tryFlow, + FlowBlock catchFlow, + int endHandler) { + if (!(catchFlow.block instanceof SequentialBlock + && catchFlow.block.getSubBlocks()[0] + instanceof InstructionBlock)) + return false; + + SequentialBlock catchBlock = (SequentialBlock) catchFlow.block; + Expression instr = + ((InstructionBlock)catchBlock.subBlocks[0]).getInstruction(); + + if (instr instanceof ComplexExpression + && ((ComplexExpression)instr).getOperator() + instanceof MonitorExitOperator + && ((ComplexExpression)instr).getSubExpressions()[0] + instanceof LocalLoadOperator + && catchBlock.subBlocks[1] instanceof ThrowBlock + && ((ThrowBlock)catchBlock.subBlocks[1]).instr + instanceof NopOperator) { + + /* This is a synchronized block: + * + * local_x = monitor object; // later + * monitorenter local_x // later + * tryFlow: + * |- syncronized block + * | ... + * | every jump to outside is preceded by jsr subroutine-, + * | ... | + * |- monitorexit local_x | + * ` jump after this block (without jsr monexit) | + * | + * catchFlow: | + * local_n = stack | + * monitorexit local_x | + * throw local_n | + * oprtional subroutine: <----------------------------------' + * astore_n + * monitorexit local_x + * return_n + */ + + /* Now remove the jump (after the throw) from the + * catch block so that we can forget about it. + */ + + catchBlock.subBlocks[1] + .jump.destination.predecessors.removeElement(catchFlow); + + ComplexExpression monexit = (ComplexExpression) + ((InstructionBlock) catchBlock.subBlocks[0]).instr; + LocalInfo local = + ((LocalLoadOperator)monexit.getSubExpressions()[0]) + .getLocalInfo(); + tryFlow.length += catchFlow.length; + + checkAndRemoveMonitorExit + (tryFlow, local, catchFlow.addr+catchFlow.length, endHandler); + + SynchronizedBlock syncBlock = new SynchronizedBlock(local); + TryBlock tryBlock = (TryBlock) tryFlow.block; + syncBlock.replace(tryBlock); + syncBlock.moveJump(tryBlock.jump); + syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1 + ? tryBlock.subBlocks[0] : tryBlock); + tryFlow.lastModified = syncBlock; + return true; + } + return false; + } + + private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow, + int end) { + if (!(catchFlow.block instanceof SequentialBlock + && catchFlow.block.getSubBlocks()[0] + instanceof InstructionBlock)) + return false; + + SequentialBlock catchBlock = (SequentialBlock) catchFlow.block; + Expression instr = + ((InstructionBlock)catchBlock.subBlocks[0]).getInstruction(); + + if (catchBlock.subBlocks[1] instanceof SequentialBlock + && catchBlock.subBlocks[1].getSubBlocks()[0] + instanceof JsrBlock + && instr instanceof LocalStoreOperator + && catchBlock.subBlocks[1].getSubBlocks()[1] + instanceof ThrowBlock + && ((ThrowBlock)catchBlock.subBlocks[1] + .getSubBlocks()[1]).instr + instanceof LocalLoadOperator + && ((LocalStoreOperator) instr) + .matches((LocalLoadOperator) + ((ThrowBlock)catchBlock.subBlocks[1] + .getSubBlocks()[1]).instr)) { + + /* Wow that was complicated :-) + * But now we know that the catch block looks + * exactly like an try finally block: + * + * tryFlow: + * |- first instruction + * | ... + * | every jump to outside is preceded by jsr finally + * | ... + * | jsr finally -----------------, + * `- jump after finally | + * | + * catchFlow: (already checked) | + * local_n = stack v + * jsr finally ---------------->| + * throw local_n; | + * finally: <-----------------------' + * astore_n + * ... + * return_n + */ + + FlowBlock subRoutine = + ((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0]) + .innerBlock.jump.destination; + + /* Now remove the two jumps of the catch block + * so that we can forget about them. + * This are the jsr and the throw. + */ + catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0] + .jump.destination.predecessors.removeElement(catchFlow); + catchBlock.subBlocks[1].getSubBlocks()[1] + .jump.destination.predecessors.removeElement(catchFlow); + + subRoutine.analyze(catchFlow.addr+catchFlow.length, end); + if (!transformSubRoutine(subRoutine)) + return false; + Stack jumps = (Stack)tryFlow.successors.get(subRoutine); + if (jumps != null) + /* jumps may be null, if tryFlow ends with a throw */ + tryFlow.updateInOut(subRoutine, true, jumps); + + tryFlow.length += catchFlow.length; + checkAndRemoveJSR(tryFlow, subRoutine); + + TryBlock tryBlock = (TryBlock)tryFlow.block; + if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { + /* remove the nested tryBlock */ + TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; + innerTry.gen = tryBlock.gen; + innerTry.replace(tryBlock); + tryBlock = innerTry; + tryFlow.lastModified = innerTry; + } + FinallyBlock newBlock = new FinallyBlock(); + newBlock.setCatchBlock(subRoutine.block); + tryBlock.addCatchBlock(newBlock); + tryFlow.mergeSuccessors(subRoutine); + tryFlow.length += subRoutine.length; + return true; + } + return false; + } + + private boolean analyzeSpecialFinally(FlowBlock tryFlow, + FlowBlock catchFlow, int end) { + + StructuredBlock firstInstr = + catchFlow.block instanceof SequentialBlock + ? catchFlow.block.getSubBlocks()[0]: catchFlow.block; + + if (firstInstr instanceof InstructionBlock + && ((InstructionBlock) firstInstr).getInstruction() + instanceof PopOperator + && ((PopOperator) ((InstructionBlock) firstInstr) + .getInstruction()).getCount() == 1) { + + /* This is a special try/finally-block, where + * the finally block ends with a break, return or + * similar. + */ + + FlowBlock succ = (firstInstr.jump != null) ? + firstInstr.jump.destination : null; + boolean hasExit = false; + Enumeration keys = tryFlow.successors.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (key == succ) + continue; + if (key != tryFlow.END_OF_METHOD) { + /* There is another exit in the try block, bad */ + return false; + } + Enumeration enum = + ((Stack)tryFlow.successors.get(key)).elements(); + while (enum.hasMoreElements()) { + Jump throwJump= (Jump)enum.nextElement(); + if (!(throwJump.prev instanceof ThrowBlock)) { + /* There is a return exit in the try block */ + return false; + } + } + } + + /* remove the pop now */ + firstInstr.removeBlock(); + + if (succ != null) { + Stack jumps = (Stack) tryFlow.successors.remove(succ); + succ.predecessors.removeElement(tryFlow); + /* Handle the jumps in the tryFlow. + */ + tryFlow.updateInOut(succ, true, jumps); + tryFlow.optimizeJumps(jumps, succ); + tryFlow.resolveRemaining(jumps); + } + + tryFlow.length += catchFlow.length; + + TryBlock tryBlock = (TryBlock)tryFlow.block; + if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { + /* remove the unnecessary tryBlock */ + TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; + innerTry.gen = tryBlock.gen; + innerTry.replace(tryBlock); + tryBlock = innerTry; + tryFlow.lastModified = innerTry; + } + FinallyBlock newBlock = new FinallyBlock(); + tryBlock.addCatchBlock(newBlock); + /* try block has no successors */ + + if (succ != null && succ.predecessors.size() == 1) { + while (succ.analyze(catchFlow.addr+catchFlow.length, end)); + tryFlow.length += succ.length; + tryFlow.successors.remove(succ); + newBlock.setCatchBlock(succ.block); + tryFlow.mergeSuccessors(succ); + } else { + /* Put the catchBlock in instaed. + */ + newBlock.setCatchBlock(catchFlow.block); + tryFlow.mergeSuccessors(catchFlow); + } + return true; + } + return false; + } + + /** + * Analyzes all exception handlers to try/catch/finally or + * synchronized blocks. + */ + public void analyze() { + /* Check if try/catch ranges are okay. The following succeeds + * for all classes generated by the sun java compiler, but hand + * optimized classes will fail this. + */ + for (int i=0; i= end || handler < end) + throw new AssertError("ExceptionHandler order failed: not " + + start + " < " + end + " <= " + + handler); + if (i == 0 + || startPCs[i-1] != start || endPCs[i-1] != end) { + /* The last handler does catch another range. */ + if ( /*handler > end + 1 || */ + (i > 0 && end > startPCs[i-1] && end < endPCs[i-1])) + throw new AssertError("ExceptionHandler" + + " at wrong position: " + + "end: "+end + " handler: "+handler + + (i>0 ? " last: ("+startPCs[i-1] + +", "+endPCs[i-1]+", " + +handlerPCs[i-1]+")" + :"")); + } + } + + for (int i=0; i handlerPCs[i]) + ? endPCs[i+1] + : Integer.MAX_VALUE; + if (jode.Decompiler.debugAnalyze) + System.err.println("analyzeCatch(" + startPCs[i] + ", " + + endPCs[i] + ", " +handlerPCs[i] + ")"); + FlowBlock tryFlow = flows[startPCs[i]]; + while (tryFlow.analyze(startPCs[i], handlerPCs[i])); + + if (i == 0 + || startPCs[i-1] != startPCs[i] || endPCs[i-1] != endPCs[i]) { + /* The last handler does catch another range. + * Create a new try block. + */ + TryBlock tryBlock = new TryBlock(tryFlow); + } else if (! (tryFlow.block instanceof TryBlock)) + throw new AssertError("no TryBlock"); + + FlowBlock catchFlow = flows[handlerPCs[i]]; + while (catchFlow.analyze(handlerPCs[i], endHandler)); + + if (!catchFlow.predecessors.isEmpty()) + throw new AssertError("Handler has a predecessors"); + + updateInOutCatch(tryFlow, catchFlow); + if (types[i] != null) { + analyzeCatchBlock(types[i], tryFlow, catchFlow); + + } else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler) + && ! analyzeFinally(tryFlow, catchFlow, endHandler) + && ! analyzeSpecialFinally(tryFlow, catchFlow, + endHandler)) + + analyzeCatchBlock(jode.Type.tObject, tryFlow, catchFlow); + + tryFlow.checkConsistent(); + if (jode.Decompiler.debugAnalyze) + System.err.println("analyzeCatch(" + tryFlow.addr + ", " + + (tryFlow.addr + tryFlow.length) + + ") done."); + } + } +} diff --git a/jode/jode/flow/TryBlock.java b/jode/jode/flow/TryBlock.java new file mode 100644 index 0000000..5e8b9ad --- /dev/null +++ b/jode/jode/flow/TryBlock.java @@ -0,0 +1,103 @@ +/* jode.flow.TryBlock (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.flow; +import jode.TabbedPrintWriter; + +/** + * A TryBlock is created for each exception in the + * ExceptionHandlers-Attribute.

+ * + * For each catch block (there may be more than one catch block + * appending a single try block) and for each finally and each + * synchronized block such a TryBlock is created. The + * finally/synchronized-blocks have a null exception type so that they + * are easily distinguishable from the catch blocks.

+ * + * A TryBlock is an intermediate representation that gets + * converted later to a CatchBlock, a FinallyBlock or a + * SynchronizedBlock (after the body is parsed). + * + * @date 1998/09/16 + * @author Jochen Hoenicke + * @see CatchBlock + * @see FinallyBlock + * @see SynchronizedBlock + */ + +public class TryBlock extends StructuredBlock { + + VariableSet gen; + StructuredBlock[] subBlocks = new StructuredBlock[1]; + + public TryBlock(FlowBlock tryFlow) { + this.gen = (VariableSet) tryFlow.gen.clone(); + this.flowBlock = tryFlow; + + StructuredBlock bodyBlock = tryFlow.block; + replace(bodyBlock); + this.subBlocks = new StructuredBlock[] { bodyBlock }; + bodyBlock.outer = this; + tryFlow.lastModified = this; + } + + public void addCatchBlock(CatchBlock catchBlock) { + StructuredBlock[] newSubBlocks = + new StructuredBlock[subBlocks.length+1]; + System.arraycopy(subBlocks, 0, newSubBlocks, 0, subBlocks.length); + newSubBlocks[subBlocks.length] = catchBlock; + subBlocks = newSubBlocks; + catchBlock.outer = this; + catchBlock.setFlowBlock(flowBlock); + } + + /** + * 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