KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jode > flow > TransformExceptionHandlers


1 /* TransformExceptionHandlers Copyright (C) 1998-2002 Jochen Hoenicke.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; see the file COPYING.LESSER. If not, write to
15  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  * $Id: TransformExceptionHandlers.java.in,v 4.4.2.6 2002/05/28 17:34:09 hoenicke Exp $
18  */

19
20 package jode.flow;
21 import jode.AssertError;
22 import jode.GlobalOptions;
23 import jode.type.Type;
24 import jode.decompiler.LocalInfo;
25 import jode.expr.*;
26
27 import java.util.TreeSet JavaDoc;
28 import java.util.SortedSet JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.lang.Comparable JavaDoc;
33
34 /**
35  *
36  * @author Jochen Hoenicke
37  */

38 public class TransformExceptionHandlers {
39     SortedSet JavaDoc handlers;
40     
41     static class Handler implements Comparable JavaDoc {
42     FlowBlock start;
43     int endAddr;
44     FlowBlock handler;
45     Type type;
46
47     public Handler(FlowBlock tryBlock, int end,
48                FlowBlock catchBlock, Type type) {
49         this.start = tryBlock;
50         this.endAddr = end;
51         this.handler = catchBlock;
52         this.type = type;
53     }
54
55     public int compareTo (Object JavaDoc o) {
56         Handler second = (Handler) o;
57
58         /* First sort by start offsets, highest address first...*/
59         if (start.getAddr() != second.start.getAddr())
60         /* this subtraction is save since addresses are only 16 bit */
61         return second.start.getAddr() - start.getAddr();
62
63         /* ...Second sort by end offsets, lowest address first...
64          * this will move the innermost blocks to the beginning. */

65         if (endAddr != second.endAddr)
66         return endAddr - second.endAddr;
67
68         /* ...Last sort by handler offsets, lowest first */
69         if (handler.getAddr() != second.handler.getAddr())
70         return handler.getAddr() - second.handler.getAddr();
71         
72         /* ...Last sort by typecode signature. Shouldn't happen to often.
73          */

74         if (type == second.type)
75         return 0;
76         if (type == null)
77         return -1;
78         if (second.type == null)
79         return 1;
80         return type.getTypeSignature()
81         .compareTo(second.type.getTypeSignature());
82     }
83     }
84
85     public TransformExceptionHandlers() {
86     handlers = new TreeSet JavaDoc();
87     }
88
89     /**
90      * Add an exception Handler.
91      * @param start The start address of the exception range.
92      * @param end The end address of the exception range + 1.
93      * @param handler The address of the handler.
94      * @param type The type of the exception, null for ALL.
95      */

96     public void addHandler(FlowBlock tryBlock, int end,
97                FlowBlock catchBlock, Type type) {
98     handlers.add(new Handler(tryBlock, end, catchBlock, type));
99     }
100
101
102     /**
103      * Merge the try flow block with the catch flow block. This is a kind
104      * of special T2 transformation, as all jumps to the catch block are
105      * implicit (exception can be thrown everywhere). <br>
106      *
107      * This method doesn't actually merge the contents of the blocks. The
108      * caller should do it right afterwards. <br>
109      *
110      * The flow block catchFlow mustn't have any predecessors.
111      * @param tryFlow the flow block containing the try.
112      * @param catchFlow the flow block containing the catch handler.
113      */

114     static void mergeTryCatch(FlowBlock tryFlow, FlowBlock catchFlow) {
115     if ((GlobalOptions.debuggingFlags
116          & GlobalOptions.DEBUG_ANALYZE) != 0)
117         GlobalOptions.err.println
118         ("mergeTryCatch(" + tryFlow.getAddr()
119          + ", " + catchFlow.getAddr() + ")");
120     tryFlow.updateInOutCatch(catchFlow);
121     tryFlow.mergeSuccessors(catchFlow);
122     tryFlow.mergeAddr(catchFlow);
123     }
124
125                                           
126     /**
127      * Analyzes a simple try/catch block. The try and catch part are both
128      * analyzed, the try block is already created, but the catch block
129      * isn't. <br>
130      * The catchFlow block mustn't have any predecessors.
131      *
132      * @param type The type of the exception which is caught.
133      * @param tryFlow The flow block containing the try. The contained
134      * block must be a try block.
135      * @param catchFlow the flow block containing the catch handler.
136      */

137     static void analyzeCatchBlock(Type type, FlowBlock tryFlow,
138                   FlowBlock catchFlow) {
139     /* Merge try and catch flow blocks */
140     mergeTryCatch(tryFlow, catchFlow);
141
142     /* Insert catch block into tryFlow */
143         CatchBlock newBlock = new CatchBlock(type);
144         ((TryBlock)tryFlow.block).addCatchBlock(newBlock);
145         newBlock.setCatchBlock(catchFlow.block);
146     }
147
148     /* And now the complicated parts. */
149     
150     /**
151      * This transforms a sub routine, i.e. it checks if the beginning
152      * local assignment matches the final ret and removes both. It also
153      * accepts sub routines that just pop their return address.
154      */

155     boolean transformSubRoutine(StructuredBlock subRoutineBlock) {
156     StructuredBlock firstBlock = subRoutineBlock;
157     if (firstBlock instanceof SequentialBlock)
158         firstBlock = subRoutineBlock.getSubBlocks()[0];
159
160     LocalInfo local = null;
161     if (firstBlock instanceof SpecialBlock) {
162         SpecialBlock popBlock
163         = (SpecialBlock) firstBlock;
164         if (popBlock.type != SpecialBlock.POP
165         || popBlock.count != 1)
166         return false;
167     } else if (firstBlock instanceof InstructionBlock) {
168         Expression expr
169         = ((InstructionBlock) firstBlock).getInstruction();
170         if (expr instanceof StoreInstruction
171         && ((StoreInstruction)
172             expr).getLValue() instanceof LocalStoreOperator) {
173         LocalStoreOperator store = (LocalStoreOperator)
174             ((StoreInstruction)expr).getLValue();
175         local = store.getLocalInfo();
176         expr = ((StoreInstruction) expr).getSubExpressions()[1];
177         }
178         if (!(expr instanceof NopOperator))
179         return false;
180     } else
181         return false;
182
183     /* We are now committed and can start changing code. Remove
184      * the first Statement which stores/removes the return
185      * address.
186      */

187     firstBlock.removeBlock();
188         
189     /* We don't check if there is a RET in the middle.
190      *
191      * This is a complicated task which isn't needed for javac nor
192      * jikes. We just check if the last instruction is a ret and
193      * remove this. This will never produce code with wrong semantic,
194      * as long as the bytecode was verified correctly.
195      */

196         while (subRoutineBlock instanceof SequentialBlock)
197             subRoutineBlock = subRoutineBlock.getSubBlocks()[1];
198
199         if (subRoutineBlock instanceof RetBlock
200             && (((RetBlock) subRoutineBlock).local.equals(local))) {
201         subRoutineBlock.removeBlock();
202     }
203     return true;
204     }
205
206     /**
207      * Remove the locale that javac introduces to temporary store the return
208      * value, when it executes a finally block resp. monitorexit
209      * @param ret the ReturnBlock.
210      */

211     private void removeReturnLocal(ReturnBlock ret) {
212     StructuredBlock pred = getPredecessor(ret);
213     if (!(pred instanceof InstructionBlock))
214         return;
215     Expression instr = ((InstructionBlock) pred).getInstruction();
216     if (!(instr instanceof StoreInstruction))
217         return;
218
219     Expression retInstr = ret.getInstruction();
220     if (!(retInstr instanceof LocalLoadOperator
221           && ((StoreInstruction) instr).lvalueMatches
222           ((LocalLoadOperator) retInstr)))
223         return;
224
225     Expression rvalue = ((StoreInstruction) instr).getSubExpressions()[1];
226     ret.setInstruction(rvalue);
227     ret.replace(ret.outer);
228     }
229
230     /**
231      * Remove the JSRs jumping to the specified subRoutine. The right
232      * JSRs are marked and we can just remove them. For the other JSR
233      * instructions we replace them with a warning.
234      * @param tryFlow the FlowBlock of the try block.
235      * @param subRoutine the FlowBlock of the sub routine.
236      */

237     private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
238         for (Jump jumps = tryFlow.removeJumps(subRoutine);
239          jumps != null; jumps = jumps.next) {
240
241             StructuredBlock prev = jumps.prev;
242         prev.removeJump();
243
244             if (prev instanceof EmptyBlock
245                 && prev.outer instanceof JsrBlock
246         && ((JsrBlock) prev.outer).isGood()) {
247         StructuredBlock next = prev.outer.getNextBlock();
248         prev.outer.removeBlock();
249         if (next instanceof ReturnBlock)
250             removeReturnLocal((ReturnBlock) next);
251         } else {
252         /* We have a jump to the subroutine, that is badly placed.
253          * We complain here.
254          */

255         DescriptionBlock msg = new DescriptionBlock
256             ("ERROR: invalid jump to finally block!");
257         prev.appendBlock(msg);
258         }
259         }
260     }
261     
262     private static StructuredBlock getPredecessor(StructuredBlock stmt)
263     {
264     if (stmt.outer instanceof SequentialBlock) {
265         SequentialBlock seq = (SequentialBlock) stmt.outer;
266         if (seq.subBlocks[1] == stmt)
267         return seq.subBlocks[0];
268         else if (seq.outer instanceof SequentialBlock)
269         return seq.outer.getSubBlocks()[0];
270     }
271     return null;
272     }
273
274     /**
275      * Gets the slot of the monitorexit instruction instr in the
276      * stmt, or -1 if stmt isn't a InstructionBlock with a
277      * monitorexit instruction.
278      * @param stmt the stmt, may be null.
279      */

280     private static int getMonitorExitSlot(StructuredBlock stmt) {
281     if (stmt instanceof InstructionBlock) {
282         Expression instr = ((InstructionBlock) stmt).getInstruction();
283         if (instr instanceof MonitorExitOperator) {
284         MonitorExitOperator monExit = (MonitorExitOperator)instr;
285         if (monExit.getFreeOperandCount() == 0
286             && (monExit.getSubExpressions()[0]
287             instanceof LocalLoadOperator))
288         return ((LocalLoadOperator) monExit.getSubExpressions()[0])
289                     .getLocalInfo().getSlot();
290             }
291         }
292         return -1;
293     }
294     
295     private boolean isMonitorExitSubRoutine(FlowBlock subRoutine,
296                         LocalInfo local) {
297     if (transformSubRoutine(subRoutine.block)
298         && getMonitorExitSlot(subRoutine.block) == local.getSlot())
299         return true;
300     return false;
301     }
302
303     private static StructuredBlock skipFinExitChain(StructuredBlock block)
304     {
305     StructuredBlock pred, result;
306     if (block instanceof ReturnBlock)
307         pred = getPredecessor(block);
308     else
309         pred = block;
310     result = null;
311
312     while (pred instanceof JsrBlock
313            || getMonitorExitSlot(pred) >= 0) {
314         result = pred;
315         pred = getPredecessor(pred);
316     }
317     return result;
318     }
319                     
320                     
321     private void checkAndRemoveJSR(FlowBlock tryFlow,
322                    FlowBlock subRoutine,
323                    int startOutExit, int endOutExit) {
324         Iterator JavaDoc iter = tryFlow.getSuccessors().iterator();
325     dest_loop:
326         while (iter.hasNext()) {
327         FlowBlock dest = (FlowBlock) iter.next();
328             if (dest == subRoutine)
329                 continue dest_loop;
330
331         boolean isFirstJump = true;
332             for (Jump jumps = tryFlow.getJumps(dest);
333          jumps != null; jumps = jumps.next, isFirstJump = false) {
334
335                 StructuredBlock prev = jumps.prev;
336                 if (prev instanceof ThrowBlock) {
337                     /* The jump is a throw. We have a catch-all block
338                      * that will do the finally.
339                      */

340                     continue;
341                 }
342                 if (prev instanceof EmptyBlock
343                     && prev.outer instanceof JsrBlock) {
344                     /* This jump is a jsr, since it doesn't leave the
345              * block forever, we can ignore it.
346              */

347             continue;
348                 }
349         StructuredBlock pred = skipFinExitChain(prev);
350
351         if (pred instanceof JsrBlock) {
352             JsrBlock jsr = (JsrBlock) pred;
353             StructuredBlock jsrInner = jsr.innerBlock;
354             if (jsrInner instanceof EmptyBlock
355             && jsrInner.jump != null
356             && jsrInner.jump.destination == subRoutine) {
357             /* The jump is preceeded by the right jsr. Mark the
358              * jsr as good.
359              */

360             jsr.setGood(true);
361             continue;
362             }
363         }
364
365         if (pred == null && isFirstJump) {
366             /* Now we have a jump that is not preceded by any
367              * jsr. There's a last chance: the jump jumps
368              * directly to a correct jsr instruction, which
369              * lies outside the try/catch block.
370              */

371             if (jumps.destination.predecessors.size() == 1
372             && jumps.destination.getAddr() >= startOutExit
373             && jumps.destination.getNextAddr() <= endOutExit) {
374             jumps.destination.analyze(startOutExit, endOutExit);
375             
376             StructuredBlock sb = jumps.destination.block;
377             if (sb instanceof SequentialBlock)
378                 sb = sb.getSubBlocks()[0];
379             if (sb instanceof JsrBlock
380                 && sb.getSubBlocks()[0] instanceof EmptyBlock
381                 && (sb.getSubBlocks()[0].jump.destination
382                 == subRoutine)) {
383                 StructuredBlock jsrInner = sb.getSubBlocks()[0];
384                 jumps.destination.removeSuccessor(jsrInner.jump);
385                 jsrInner.removeJump();
386                 sb.removeBlock();
387                 continue dest_loop;
388             }
389             }
390         }
391         
392                 /* Now we have a jump with a wrong destination.
393                  * Complain!
394                  */

395                 DescriptionBlock msg
396                     = new DescriptionBlock("ERROR: no jsr to finally");
397         if (pred != null)
398             pred.prependBlock(msg);
399         else {
400             prev.appendBlock(msg);
401             msg.moveJump(prev.jump);
402         }
403             }
404         }
405     if (tryFlow.getSuccessors().contains(subRoutine))
406         removeJSR(tryFlow, subRoutine);
407     }
408
409     private void checkAndRemoveMonitorExit(FlowBlock tryFlow,
410                        LocalInfo local,
411                        int start, int end) {
412         FlowBlock subRoutine = null;
413         Iterator JavaDoc succs = tryFlow.getSuccessors().iterator();
414     dest_loop:
415         while (succs.hasNext()) {
416         boolean isFirstJump = true;
417         FlowBlock successor = (FlowBlock) succs.next();
418             for (Jump jumps = tryFlow.getJumps(successor);
419                  jumps != null; jumps = jumps.next, isFirstJump = false) {
420
421                 StructuredBlock prev = jumps.prev;
422                 if (prev instanceof ThrowBlock) {
423                     /* The jump is a throw. We have a catch-all block
424                      * that will do the finally.
425                      */

426                     continue;
427                 }
428                 if (prev instanceof EmptyBlock
429                     && prev.outer instanceof JsrBlock) {
430                     /* This jump is really a jsr, since it doesn't
431              * leave the block forever, we can ignore it.
432              */

433             continue;
434                 }
435         StructuredBlock pred = skipFinExitChain(prev);
436         if (pred instanceof JsrBlock) {
437             JsrBlock jsr = (JsrBlock) pred;
438             StructuredBlock jsrInner = jsr.innerBlock;
439             if (jsrInner instanceof EmptyBlock
440             && jsrInner.jump != null) {
441             FlowBlock dest = jsrInner.jump.destination;
442
443             if (subRoutine == null
444                 && dest.getAddr() >= start
445                 && dest.getNextAddr() <= end) {
446                 dest.analyze(start, end);
447                 if (isMonitorExitSubRoutine(dest, local))
448                 subRoutine = dest;
449             }
450
451             if (dest == subRoutine) {
452                 /* The jump is preceeded by the right jsr.
453                  * Mark it as good.
454                  */

455                 jsr.setGood(true);
456                 continue;
457             }
458             }
459         } else if (getMonitorExitSlot(pred) == local.getSlot()) {
460             /* The jump is preceeded by the right monitor
461              * exit instruction.
462              */

463             pred.removeBlock();
464             if (prev instanceof ReturnBlock)
465             removeReturnLocal((ReturnBlock) prev);
466             continue;
467         }
468
469         if (pred == null && isFirstJump) {
470             /* Now we have a jump that is not preceded by a
471              * monitorexit. There's a last chance: the jump
472              * jumps directly to the correct monitorexit
473              * instruction, which lies outside the try/catch
474              * block.
475              */

476             if (successor.predecessors.size() == 1
477             && successor.getAddr() >= start
478             && successor.getNextAddr() <= end) {
479             successor.analyze(start, end);
480             
481             StructuredBlock sb = successor.block;
482             if (sb instanceof SequentialBlock)
483                 sb = sb.getSubBlocks()[0];
484             if (sb instanceof JsrBlock
485                 && sb.getSubBlocks()[0] instanceof EmptyBlock) {
486                 StructuredBlock jsrInner = sb.getSubBlocks()[0];
487                 FlowBlock dest = jsrInner.jump.destination;
488                 if (subRoutine == null
489                 && dest.getAddr() >= start
490                 && dest.getNextAddr() <= end) {
491                 dest.analyze(start, end);
492                 if (isMonitorExitSubRoutine(dest, local))
493                     subRoutine = dest;
494                 }
495
496                 if (subRoutine == dest) {
497                 successor.removeSuccessor(jsrInner.jump);
498                 jsrInner.removeJump();
499                 sb.removeBlock();
500                 continue dest_loop;
501                 }
502             }
503             if (getMonitorExitSlot(sb) == local.getSlot()) {
504                 sb.removeBlock();
505                 continue dest_loop;
506             }
507             }
508         }
509         
510         /* Complain!
511                  */

512                 DescriptionBlock msg
513                     = new DescriptionBlock("ERROR: no monitorexit");
514                 prev.appendBlock(msg);
515                 msg.moveJump(jumps);
516             }
517         }
518
519     if (subRoutine != null) {
520         if (tryFlow.getSuccessors().contains(subRoutine))
521         removeJSR(tryFlow, subRoutine);
522         if (subRoutine.predecessors.size() == 0)
523         tryFlow.mergeAddr(subRoutine);
524     }
525     }
526
527     private StoreInstruction getExceptionStore(StructuredBlock catchBlock) {
528         if (!(catchBlock instanceof SequentialBlock)
529             || !(catchBlock.getSubBlocks()[0] instanceof InstructionBlock))
530             return null;
531         
532         Expression instr =
533             ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
534     if (!(instr instanceof StoreInstruction))
535         return null;
536
537     StoreInstruction store = (StoreInstruction) instr;
538     if (!(store.getLValue() instanceof LocalStoreOperator
539           && store.getSubExpressions()[1] instanceof NopOperator))
540         return null;
541     
542     return store;
543     }
544
545     private boolean analyzeSynchronized(FlowBlock tryFlow,
546                                         FlowBlock catchFlow,
547                                         int endHandler) {
548     /* Check if this is a synchronized block. We mustn't change
549      * anything until we are sure.
550      */

551     StructuredBlock catchBlock = catchFlow.block;
552
553     /* Check for a optional exception store and skip it */
554     StoreInstruction excStore = getExceptionStore(catchBlock);
555     if (excStore != null)
556         catchBlock = catchBlock.getSubBlocks()[1];
557
558         /* Check for the monitorexit instruction */
559         if (!(catchBlock instanceof SequentialBlock
560               && catchBlock.getSubBlocks()[0]
561               instanceof InstructionBlock))
562             return false;
563         Expression instr =
564             ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
565         if (!(instr instanceof MonitorExitOperator
566           && instr.getFreeOperandCount() == 0
567           && (((MonitorExitOperator)instr).getSubExpressions()[0]
568           instanceof LocalLoadOperator)
569           && catchBlock.getSubBlocks()[1] instanceof ThrowBlock))
570         return false;
571
572     Expression throwInstr =
573         ((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction();
574     
575     if (excStore != null) {
576         if (!(throwInstr instanceof Operator
577           && excStore.lvalueMatches((Operator)throwInstr)))
578         return false;
579     } else {
580         if (!(throwInstr instanceof NopOperator))
581         return false;
582     }
583
584     /* This is a synchronized block:
585      *
586      * local_x = monitor object; // later
587      * monitorenter local_x // later
588      * tryFlow:
589      * |- synchronized block
590      * | ...
591      * | every jump to outside is preceded by jsr subroutine-,
592      * | ... |
593      * |- monitorexit local_x |
594      * ` jump after this block (without jsr monexit) |
595      * |
596      * catchBlock: |
597      * local_n = stack |
598      * monitorexit local_x |
599      * throw local_n |
600      * [OR ALTERNATIVELY:] |
601      * monitorexit local_x |
602      * throw stack |
603      * optional subroutine: <-----------------------------------'
604      * astore_n
605      * monitorexit local_x
606      * return_n
607      */

608
609     /* Merge try and catch flow blocks. No need to insert the
610      * catchFlow.block into the try flow though, since all its
611      * instruction are synthetic.
612      *
613      * Though we need to remove the jump of the throw
614      * instruction.
615      */

616     catchFlow.removeSuccessor(catchBlock.getSubBlocks()[1].jump);
617     mergeTryCatch(tryFlow, catchFlow);
618     
619     MonitorExitOperator monexit = (MonitorExitOperator)
620         ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr;
621     LocalInfo local =
622         ((LocalLoadOperator)monexit.getSubExpressions()[0])
623         .getLocalInfo();
624     
625     if ((GlobalOptions.debuggingFlags
626          & GlobalOptions.DEBUG_ANALYZE) != 0)
627         GlobalOptions.err.println
628         ("analyzeSynchronized(" + tryFlow.getAddr()
629          + "," + tryFlow.getNextAddr() + "," + endHandler + ")");
630     
631     checkAndRemoveMonitorExit
632         (tryFlow, local, tryFlow.getNextAddr(), endHandler);
633     
634     SynchronizedBlock syncBlock = new SynchronizedBlock(local);
635     TryBlock tryBlock = (TryBlock) tryFlow.block;
636     syncBlock.replace(tryBlock);
637     syncBlock.moveJump(tryBlock.jump);
638     syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1
639                    ? tryBlock.subBlocks[0] : tryBlock);
640     tryFlow.lastModified = syncBlock;
641     return true;
642     }
643
644     /**
645      * Merge try and finally flow blocks.
646      * @param tryFlow The try flow block. Its contained block must be
647      * a try block.
648      * @param catchFlow The catch flow block that contains the finally
649      * block.
650      * @param finallyBlock block that either contains the finally block.
651      * It is part of the catchFlow. The other parts of catchFlow are
652      * synthetic and can be removed.
653      */

654     private void mergeFinallyBlock(FlowBlock tryFlow, FlowBlock catchFlow,
655                    StructuredBlock finallyBlock) {
656     TryBlock tryBlock = (TryBlock) tryFlow.block;
657     if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
658         /* A try { try { } catch {} } finally{} is equivalent
659          * to a try {} catch {} finally {}
660          * so remove the surrounding tryBlock.
661          */

662         TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
663         innerTry.gen = tryBlock.gen;
664         innerTry.replace(tryBlock);
665         tryBlock = innerTry;
666         tryFlow.lastModified = tryBlock;
667         tryFlow.block = tryBlock;
668     }
669
670     /* Now merge try and catch flow blocks */
671     mergeTryCatch(tryFlow, catchFlow);
672     FinallyBlock newBlock = new FinallyBlock();
673     newBlock.setCatchBlock(finallyBlock);
674     tryBlock.addCatchBlock(newBlock);
675     }
676
677     private boolean analyzeFinally(FlowBlock tryFlow,
678                    FlowBlock catchFlow, int end) {
679
680     /* Layout of a try-finally block:
681      *
682      * tryFlow:
683      * |- first instruction
684      * | ...
685      * | every jump to outside is preceded by jsr finally
686      * | ...
687      * | jsr finally -----------------,
688      * `- jump after finally |
689      * |
690      * catchBlock: |
691      * local_n = stack v
692      * jsr finally ---------------->|
693      * throw local_n; |
694      * finally: <-----------------------'
695      * astore_n
696      * ...
697      * return_n
698      */

699     
700     StructuredBlock catchBlock = catchFlow.block;
701     StoreInstruction excStore = getExceptionStore(catchBlock);
702     if (excStore == null)
703         return false;
704
705     catchBlock = catchBlock.getSubBlocks()[1];
706     if (!(catchBlock instanceof SequentialBlock))
707             return false;
708         
709         StructuredBlock finallyBlock = null;
710
711         if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) {
712             /* In case the try block has no exit (that means, it
713              * throws an exception or loops forever), the finallyBlock
714              * was already merged with the catchBlock. We have to
715              * check for this case separately:
716              *
717              * do {
718              * JSR
719              * break;
720              * throw local_x
721              * } while(false);
722              * finallyBlock; (starts with POP or local_y = POP)
723              */

724             LoopBlock doWhileFalse = (LoopBlock)catchBlock.getSubBlocks()[0];
725             if (doWhileFalse.type == LoopBlock.DOWHILE
726                 && doWhileFalse.cond == LoopBlock.FALSE
727                 && doWhileFalse.bodyBlock instanceof SequentialBlock) {
728         if (transformSubRoutine(catchBlock.getSubBlocks()[1])) {
729             finallyBlock = catchBlock.getSubBlocks()[1];
730             catchBlock = (SequentialBlock) doWhileFalse.bodyBlock;
731         }
732             }
733         }
734
735         if (!(catchBlock instanceof SequentialBlock
736           && catchBlock.getSubBlocks()[0] instanceof JsrBlock
737           && catchBlock.getSubBlocks()[1] instanceof ThrowBlock))
738
739         return false;
740     
741     JsrBlock jsrBlock = (JsrBlock)catchBlock.getSubBlocks()[0];
742     ThrowBlock throwBlock = (ThrowBlock) catchBlock.getSubBlocks()[1];
743
744
745     if (!(throwBlock.getInstruction() instanceof Operator
746           && excStore.lvalueMatches((Operator)
747                     throwBlock.getInstruction())))
748         return false;
749     
750     FlowBlock subRoutine;
751     if (finallyBlock != null) {
752         /* Check if the jsr breaks (see comment above). We don't
753          * need to check if it breaks to the right block, because
754          * we know that there is only one Block around the jsr.
755          */

756         if (!(jsrBlock.innerBlock instanceof BreakBlock))
757         return false;
758         
759         /* TODO - Check if the try block has no exit (except throws)
760          */

761 // if (tryFlow.getSuccessors().size() > 0)
762
// return false;
763

764         catchBlock = finallyBlock;
765         subRoutine = null;
766         catchFlow.removeSuccessor(throwBlock.jump);
767     } else {
768         if (!(jsrBlock.innerBlock instanceof EmptyBlock))
769         return false;
770         finallyBlock = jsrBlock.innerBlock;
771         subRoutine = finallyBlock.jump.destination;
772
773         /* We are committed now and can start changing the blocks.
774          */

775         catchFlow.removeSuccessor(throwBlock.jump);
776         checkAndRemoveJSR(tryFlow, subRoutine,
777                   tryFlow.getNextAddr(), end);
778
779         /* Now analyze and transform the subroutine.
780          */

781         while (subRoutine.analyze(tryFlow.getNextAddr(), end));
782         if (subRoutine.predecessors.size() == 1) {
783         /* catchFlow is synthetic, so we can safely remove it
784          * here.
785          */

786         catchFlow.removeSuccessor(finallyBlock.jump);
787         subRoutine.mergeAddr(catchFlow);
788         catchFlow = subRoutine;
789
790         if (!transformSubRoutine(subRoutine.block)) {
791             finallyBlock = subRoutine.block;
792             DescriptionBlock msg = new DescriptionBlock
793             ("ERROR: Missing return address handling");
794             StructuredBlock subblock = subRoutine.block;
795             msg.replace(finallyBlock);
796             msg.appendBlock(finallyBlock);
797         }
798         finallyBlock = subRoutine.block;
799         }
800     }
801
802     /* Now finish it.
803      */

804     mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
805     return true;
806     }
807
808     private boolean analyzeSpecialFinally(FlowBlock tryFlow,
809                       FlowBlock catchFlow, int end) {
810     StructuredBlock finallyBlock = catchFlow.block;
811         StructuredBlock firstInstr =
812             finallyBlock instanceof SequentialBlock
813             ? finallyBlock.getSubBlocks()[0]: finallyBlock;
814
815         if (!(firstInstr instanceof SpecialBlock
816           && ((SpecialBlock)firstInstr).type == SpecialBlock.POP
817           && ((SpecialBlock)firstInstr).count == 1))
818         return false;
819
820     /* This is a special try/finally-block, where
821      * the finally block ends with a break, return or
822      * similar.
823      */

824
825     /* Make sure that resolveJump only works on the inside of the try
826      */

827     tryFlow.lastModified = tryFlow.block.getSubBlocks()[0];
828     FlowBlock finallyFlow;
829     if (finallyBlock instanceof SequentialBlock) {
830         finallyBlock = finallyBlock.getSubBlocks()[1];
831         finallyFlow = null;
832     } else {
833         finallyBlock = new EmptyBlock();
834         finallyBlock.moveJump(firstInstr.jump);
835
836         /* Handle the jumps in the tryFlow to finallyFlow.
837          */

838         finallyFlow = finallyBlock.jump.destination;
839         if (tryFlow.getSuccessors().contains(finallyFlow)) {
840         Jump jumps = tryFlow.removeJumps(finallyFlow);
841         jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow);
842         tryFlow.resolveRemaining(jumps);
843         }
844     }
845
846     /* Complain about all other jumps in try block */
847     Set JavaDoc trySuccs = tryFlow.getSuccessors();
848     for (Iterator JavaDoc i = trySuccs.iterator(); i.hasNext(); ) {
849         FlowBlock succ = (FlowBlock) i.next();
850         if (succ != FlowBlock.END_OF_METHOD) {
851         /* This seems to be a illegal succ in try block that
852          * doesn't go to the finally block. There is a last
853          * chance though. If the destination directly jumps
854          * to the try block, its okay.
855          */

856         if (succ.block instanceof EmptyBlock
857             && succ.block.jump.destination == finallyFlow) {
858             Jump jumps = tryFlow.removeJumps(succ);
859             jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow);
860             tryFlow.resolveRemaining(jumps);
861             if (succ.predecessors.size() == 0) {
862             succ.removeJumps(finallyFlow);
863             tryFlow.mergeAddr(succ);
864             }
865             continue;
866         }
867         }
868         for (Jump jumps = tryFlow.getJumps(succ);
869          jumps != null; jumps = jumps.next) {
870         if (jumps.prev instanceof ThrowBlock)
871             continue;
872         
873         DescriptionBlock msg =
874             new DescriptionBlock
875             ("ERROR: doesn't go through finally block!");
876         if (jumps.prev instanceof ReturnBlock) {
877             msg.replace(jumps.prev);
878             msg.appendBlock(jumps.prev);
879         } else {
880             jumps.prev.appendBlock(msg);
881             msg.moveJump(jumps);
882         }
883         }
884         }
885
886     mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
887     /* Following code will work be put inside the finallyBlock */
888     tryFlow.lastModified = finallyBlock;
889     return true;
890     }
891
892     void checkTryCatchOrder() {
893         /* Check if try/catch ranges are okay. The following succeeds
894          * for all classes generated by the sun java compiler, but hand
895          * optimized classes (or generated by other compilers) will fail.
896          */

897     Handler last = null;
898     for (Iterator JavaDoc i = handlers.iterator(); i.hasNext(); ) {
899         Handler exc = (Handler) i.next();
900         int start = exc.start.getAddr();
901         int end = exc.endAddr;
902         int handler = exc.handler.getAddr();
903         if (start >= end || handler < end)
904         throw new AssertError
905             ("ExceptionHandler order failed: not "
906              + start + " < " + end + " <= " + handler);
907         if (last != null
908         && (last.start.getAddr() != start || last.endAddr != end)) {
909         /* The last handler does catch another range.
910          * Due to the order:
911          * start < last.start.getAddr() || end > last.end.getAddr()
912          */

913         if (end > last.start.getAddr() && end < last.endAddr)
914             throw new AssertError
915             ("Exception handlers ranges are intersecting: ["
916              + last.start.getAddr()+", "+last.endAddr+"] and ["
917              + start+", "+end+"].");
918         }
919         last = exc;
920     }
921     }
922     
923     /**
924      * Analyzes all exception handlers to try/catch/finally or
925      * synchronized blocks.
926      */

927     public void analyze() {
928     checkTryCatchOrder();
929
930     Iterator JavaDoc i = handlers.iterator();
931     Handler exc = null;
932     Handler next = i.hasNext() ? (Handler) i.next() : null;
933     while(next != null) {
934         Handler last = exc;
935         exc = next;
936         next = i.hasNext() ? (Handler) i.next() : null;
937         int endHandler = Integer.MAX_VALUE;
938         /* If the next exception handler catches a bigger range
939          * it must surround the handler completely.
940          */

941         if (next != null && next.endAddr > exc.endAddr)
942         endHandler = next.endAddr;
943         
944         FlowBlock tryFlow = exc.start;
945         tryFlow.checkConsistent();
946         
947         if (last == null || exc.type == null
948         || last.start.getAddr() != exc.start.getAddr()
949         || last.endAddr != exc.endAddr) {
950         /* The last handler does catch another range.
951          * Create a new try block.
952          */

953         if ((GlobalOptions.debuggingFlags
954              & GlobalOptions.DEBUG_ANALYZE) != 0)
955             GlobalOptions.err.println
956             ("analyzeTry("
957              + exc.start.getAddr() + ", " + exc.endAddr+")");
958         while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr));
959         
960         TryBlock tryBlock = new TryBlock(tryFlow);
961         } else if (!(tryFlow.block instanceof TryBlock))
962         throw new AssertError("no TryBlock");
963
964         FlowBlock catchFlow = exc.handler;
965         boolean isMultiUsed = catchFlow.predecessors.size() != 0;
966         if (!isMultiUsed && next != null) {
967         for (Iterator JavaDoc j = handlers.tailSet(next).iterator();
968              j.hasNext();) {
969             Handler h = (Handler) j.next();
970             if (h.handler == catchFlow) {
971             isMultiUsed = true;
972             break;
973             }
974         }
975         }
976         
977         if (isMultiUsed) {
978         /* If this exception is used in other exception handlers,
979          * create a new flow block, that jumps to the handler.
980          * This will be our new exception handler.
981          */

982         EmptyBlock jump = new EmptyBlock(new Jump(catchFlow));
983         FlowBlock newFlow = new FlowBlock(catchFlow.method,
984                           catchFlow.getAddr());
985         newFlow.appendBlock(jump, 0);
986         catchFlow.prevByAddr.nextByAddr = newFlow;
987         newFlow.prevByAddr = catchFlow.prevByAddr;
988         newFlow.nextByAddr = catchFlow;
989         catchFlow.prevByAddr = newFlow;
990         catchFlow = newFlow;
991         } else {
992         if ((GlobalOptions.debuggingFlags
993              & GlobalOptions.DEBUG_ANALYZE) != 0)
994             GlobalOptions.err.println
995             ("analyzeCatch("
996                  + catchFlow.getAddr() + ", " + endHandler + ")");
997         while (catchFlow.analyze(catchFlow.getAddr(), endHandler));
998         }
999         
1000        if (exc.type != null)
1001        analyzeCatchBlock(exc.type, tryFlow, catchFlow);
1002        
1003        else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler)
1004             && ! analyzeFinally(tryFlow, catchFlow, endHandler)
1005             && ! analyzeSpecialFinally(tryFlow, catchFlow,
1006                        endHandler))
1007        
1008        analyzeCatchBlock(Type.tObject, tryFlow, catchFlow);
1009        
1010        tryFlow.checkConsistent();
1011        if ((GlobalOptions.debuggingFlags
1012         & GlobalOptions.DEBUG_ANALYZE) != 0)
1013        GlobalOptions.err.println
1014            ("analyzeTryCatch(" + tryFlow.getAddr() + ", "
1015             + tryFlow.getNextAddr() + ") done.");
1016    }
1017    }
1018}
1019
Popular Tags