git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@78 379699f6-c40d-0410-875b-85095c16579estable
parent
e2b966818c
commit
dff23235cc
@ -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<count; i++) { |
||||||
|
int start = startPCs[i]; |
||||||
|
int end = endPCs[i]; |
||||||
|
int handler = handlerPCs[i]; |
||||||
|
if (start >= 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<count; i++) { |
||||||
|
int endHandler = (i< count-1 && endPCs[i+1] > 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."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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. <p> |
||||||
|
* |
||||||
|
* 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. <p> |
||||||
|
* |
||||||
|
* 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<subBlocks.length; 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; |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpInstruction(TabbedPrintWriter writer) |
||||||
|
throws java.io.IOException { |
||||||
|
writer.println("try {"); |
||||||
|
writer.tab(); |
||||||
|
subBlocks[0].dumpSource(writer); |
||||||
|
writer.untab(); |
||||||
|
for (int i=1; i<subBlocks.length;i++) |
||||||
|
subBlocks[i].dumpSource(writer); |
||||||
|
writer.println("}"); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue