1 // Copyright (c) 2008 Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.bytecode;
5 
6 /** Support for code block that one may be exited.
7  * You may optionally exit with a value, and/or run intervening finally blocks.
8  *
9  * <p>Typical usage::
10  * <blockquote><pre>
11  * CodeAttr code = ...;
12  * Type retType = ...; // a Type or null
13  * ExitableBlock block = code.startExitableBlock(retType, true);
14  * ...
15  * ... block.exit() ...; // conditionally
16  * ...
17  * code.endExitableBlock();
18  * </pre></blockquote>
19  *
20  * <p>The <code>block.exit()</code> compiles to a transfer to the end
21  * of the <code>block</code>, executing any <code>finally</code>-blocks
22  * within the block that surround the call to <code>exit</code>.
23  *
24  * <p>If the <code>ExitableBlock</code> should leave a result on
25  * the stack, then specify the type of the result as the <code>retType</code>.
26  * The block itself must push a result before calling
27  * <code>endExitableBlock</code> (unless <code>code.reachableHere()</code>
28  * is false), and must also push a result before <code>block.exit().</code>.
29  */
30 
31 public class ExitableBlock
32 {
33   Variable resultVariable;
34   CodeAttr code;
35   Type resultType;
36   TryState initialTryState;
37   Label endLabel;
38   ExitableBlock outer;
39   // The innermost current TryState which contains an exit to the block.
40   TryState currentTryState;
41   // Next ExitableBlock in list headed by currentTryState.exitCases.
42   ExitableBlock nextCase;
43   int switchCase;
44   int startStackSize;
45 
ExitableBlock(Type resultType, CodeAttr code, boolean runFinallyBlocks)46   ExitableBlock (Type resultType, CodeAttr code, boolean runFinallyBlocks)
47   {
48     this.code = code;
49     this.resultType = resultType;
50     this.startStackSize = code.SP;
51     initialTryState = code.try_stack;
52     if (runFinallyBlocks && resultType != null)
53       {
54         code.pushScope();
55         Variable var = code.addLocal(resultType);
56         resultVariable = var;
57         // We need to initialize the variable for the sake of the verifier.
58         code.emitStoreDefaultValue(var);
59         switchCase = ++code.exitableBlockLevel;
60       }
61     endLabel = new Label(code);
62   }
63 
finish()64   void finish ()
65   {
66     boolean reachable = code.reachableHere();
67     if (resultVariable != null && reachable)
68       code.emitStore(resultVariable);
69     endLabel.define(code);
70     if (! reachable && ! endLabel.needsStackMapEntry)
71       code.setUnreachable();
72     else if (resultVariable != null)
73       code.emitLoad(resultVariable);
74     if (resultVariable != null)
75       {
76         code.popScope();
77         --code.exitableBlockLevel;
78       }
79   }
80 
81   /** Exit this surrounding block, executing finally blocks as needed.
82    * Return a value as the result of this ExitableBlock. */
exit()83   public void exit ()
84   {
85     if (resultVariable != null)
86       code.emitStore(resultVariable);
87     exit(TryState.outerHandler(code.try_stack, initialTryState));
88   }
89 
90   /** If an exit is simple, return the label for block end.
91    * The exit is simple if there is no intervening finally blocks.
92    */
exitIsGoto()93   public Label exitIsGoto ()
94   {
95     if (TryState.outerHandler(code.try_stack, initialTryState) == initialTryState)
96       return endLabel;
97     else
98       return null;
99   }
100 
popStack(CodeAttr code)101     private void popStack(CodeAttr code) {
102         int retSize = resultVariable != null || resultType == null ? 0
103             : resultType.size > 4 ? 2 : 1;
104         if (code.SP == startStackSize + retSize)
105             return;
106         Variable resultVar;
107         if (retSize > 0) {
108             code.pushScope();
109             resultVar = code.addLocal(resultType);
110             code.emitStore(resultVar);
111         }
112         else
113             resultVar = null;
114         code.emitPop(code.SP - startStackSize);
115         if (resultVar != null) {
116             code.emitLoad(resultVar);
117             code.popScope();
118         }
119     }
120 
121   /** Exit this surrounding block, executing finally blocks as needed. */
exit(TryState activeTry)122   void exit (TryState activeTry)
123   {
124     if (! code.reachableHere())
125       return;
126     popStack(code);
127     if (activeTry == initialTryState)
128       code.emitGoto(endLabel);
129     else if (code.useJsr())
130       {
131         for (TryState stack = code.try_stack;
132              stack != initialTryState; stack = stack.previous)
133           {
134             if (stack.finally_subr != null         // there is a finally block
135                 && stack.finally_ret_addr == null) // 'return' is not inside it
136               {
137                 code.emitJsr(stack.finally_subr);
138               }
139           }
140         code.emitGoto(endLabel);
141       }
142     else
143       {
144         if (currentTryState == null)
145           linkCase(activeTry);
146         if (activeTry.saved_result != null)
147           code.emitStoreDefaultValue(activeTry.saved_result);
148         code.emitPushInt(switchCase);
149         code.emitPushNull(); // No caught Throwable.
150         code.emitGoto(activeTry.finally_subr);
151       }
152   }
153 
linkCase(TryState tryState)154   void linkCase (TryState tryState)
155   {
156     if (currentTryState != tryState)
157       {
158         if (currentTryState != null)
159           throw new Error();
160         nextCase = tryState.exitCases;
161         tryState.exitCases = this;
162         currentTryState = tryState;
163       }
164   }
165 }
166