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