| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.royale.compiler.internal.as.codegen; |
| |
| import java.util.Vector; |
| |
| import org.apache.royale.abc.instructionlist.InstructionList; |
| import org.apache.royale.abc.semantics.Label; |
| |
| import static org.apache.royale.abc.ABCConstants.*; |
| |
| |
| /** |
| * An ExceptionHandlingContext manages the tree-crossing |
| * state of a try/catch/finally composite statement; its |
| * actual processing state (try, catch, and finally have |
| * slightly different requirements), and most importantly |
| * the return instruction fragments for the finally block's |
| * "callers." |
| */ |
| public final class ExceptionHandlingContext extends ControlFlowContext |
| { |
| |
| /** |
| * @param flow_mgr - the flow manager that owns this context. |
| */ |
| ExceptionHandlingContext( ControlFlowContextManager flow_mgr) |
| { |
| super(null); |
| this.flowMgr = flow_mgr; |
| } |
| |
| /** |
| * The flow manager that owns this context. |
| * Used to find the associated LexicalScope and |
| * allocate temporaries. |
| */ |
| ControlFlowContextManager flowMgr; |
| |
| /** |
| * The finally block's label, if there is a finally block. |
| */ |
| Label finallyBlock = null; |
| |
| /** |
| * The "error return" from the finally. |
| */ |
| Label finallyDoRethrow = null; |
| |
| /** |
| * The "normal return" from the finally. |
| */ |
| Label finallyDoFallthrough = null; |
| |
| /** |
| * A local set to a distinct value by each caller; |
| * corresponds with the position of that caller's |
| * finally return fragment in the returns array. |
| */ |
| Binding finallyReturnStorage = null; |
| |
| /** |
| * A local used to store the exception scope; needed to |
| * restore nested exception scopes in an inner catch. |
| */ |
| Binding exceptionStorage = null; |
| |
| /** |
| * The possible states of the exception-handling context. |
| * There's an edge from TRY to FINALLY, from FINALLY to |
| * CATCH, and from TRY to CATCH (if there's no FINALLY). |
| * @see #setFinallyControlState(boolean) |
| * @see #setCatchControlState(boolean) |
| */ |
| enum TryCatchFinallyState |
| { |
| INITIAL, |
| TRY, |
| FINALLY, |
| CATCH |
| }; |
| |
| /** |
| * This exception-handling context's state. |
| */ |
| TryCatchFinallyState tryCatchFinallyState = TryCatchFinallyState.INITIAL; |
| |
| /** |
| * Finally return fragments and their labels. |
| * The labels go into the finally's concluding |
| * lookupswitch a.k.a. computed GOTO instruction. |
| */ |
| public static class FinallyReturn |
| { |
| Label finallyLabel; |
| InstructionList finallyInsns; |
| |
| FinallyReturn(Label label, InstructionList list) |
| { |
| this.finallyLabel = label; |
| this.finallyInsns = list; |
| } |
| |
| Label getLabel() |
| { |
| return finallyLabel; |
| } |
| |
| InstructionList getInstructions() |
| { |
| return finallyInsns; |
| } |
| }; |
| |
| /** |
| * The marshalled finally returns. |
| * @warn order is significant; the order here must match |
| * the order of the callers' "return index" saved into |
| * the finallyReturnStorage local. |
| * @see finallyReturnStorage |
| */ |
| Vector<FinallyReturn> finallyReturns = null; |
| |
| /** |
| * true => this exception handling context has a finally block. |
| */ |
| boolean hasFinally = false; |
| |
| @Override |
| InstructionList addExitPath(InstructionList exitBranch) |
| { |
| InstructionList result = exitBranch; |
| |
| // Enter any finally blocks |
| // that are not already active. |
| if ( hasFinallyBlock() ) |
| { |
| if ( ! isActiveFinally() ) |
| result = addFinallyReturn(result); |
| |
| } |
| |
| // Pop the scope of any active catch blocks. |
| if ( isActiveCatchBlock() ) |
| { |
| InstructionList catch_fixup = new InstructionList(); |
| catch_fixup.addInstruction(OP_popscope); |
| catch_fixup.addInstruction(getExceptionStorage().kill()); |
| |
| catch_fixup.addAll(result); |
| result = catch_fixup; |
| } |
| return result; |
| } |
| |
| @Override |
| void addExceptionHandlerEntry(InstructionList exceptionHandler) |
| { |
| if ( isActiveCatchBlock() ) |
| { |
| exceptionHandler.addInstruction(getExceptionStorage().getlocal()); |
| exceptionHandler.addInstruction(OP_pushscope); |
| } |
| } |
| |
| |
| |
| /** |
| * Add a return fragment to the active finally clause. |
| * @param retblock - the instructions that make up the |
| * finally return sequence. |
| * @return the substitute return sequence that sets up |
| * the finallyReturnStorage local and jumps to the |
| * finally block. |
| */ |
| private InstructionList addFinallyReturn(InstructionList retblock) |
| { |
| assert(this.finallyReturns != null): "Not a finally context."; |
| |
| Label retblock_label = new Label(); |
| retblock.labelFirst(retblock_label); |
| finallyReturns.add(new FinallyReturn(retblock_label, retblock)); |
| |
| InstructionList result = new InstructionList(); |
| CmcEmitter.pushNumericConstant(finallyReturns.size(), result); |
| result.addInstruction(OP_coerce_a); |
| result.addInstruction(finallyReturnStorage.setlocal()); |
| result.addInstruction(OP_jump, finallyBlock); |
| return result; |
| } |
| |
| /** |
| * @return this exception-handling context's exceptionStorage local. |
| * @note generated on demand. |
| */ |
| public Binding getExceptionStorage() |
| { |
| if ( this.exceptionStorage == null ) |
| this.exceptionStorage = flowMgr.currentScope.allocateTemp(); |
| return this.exceptionStorage; |
| } |
| |
| /** |
| * Transition from the initial state into the try processing state. There is |
| * no method to end the try control state because the BURM does not have |
| * reduction for the try block, instead we implicitly end the try processing |
| * state when we start a finally or catch processing state. |
| */ |
| void startTryControlState() |
| { |
| assert ( this.tryCatchFinallyState == TryCatchFinallyState.INITIAL ); |
| this.tryCatchFinallyState = TryCatchFinallyState.TRY; |
| } |
| |
| /** |
| * Transition into the finally processing state. |
| */ |
| void startFinallyControlState() |
| { |
| assert ( this.tryCatchFinallyState == TryCatchFinallyState.TRY ); |
| this.tryCatchFinallyState = TryCatchFinallyState.FINALLY; |
| } |
| |
| /** |
| * Transition out of the finally processing state into |
| * the catch state. |
| */ |
| void endFinallyControlState() |
| { |
| assert ( this.tryCatchFinallyState == TryCatchFinallyState.FINALLY ); |
| this.tryCatchFinallyState = TryCatchFinallyState.CATCH; |
| } |
| |
| /** |
| * Enter the catch control state. |
| */ |
| void startCatchControlState() |
| { |
| // All of the three processing states |
| // are valid previous states. |
| this.tryCatchFinallyState = TryCatchFinallyState.CATCH; |
| } |
| |
| void endCatchControlState() |
| { |
| assert (TryCatchFinallyState.CATCH == this.tryCatchFinallyState) : |
| "leaving catch, but control state is " + this.tryCatchFinallyState; |
| } |
| |
| /** |
| * @return true if the exception handling context is in |
| * catch processing state. |
| */ |
| private boolean isActiveCatchBlock() |
| { |
| return TryCatchFinallyState.CATCH == this.tryCatchFinallyState; |
| } |
| |
| /** |
| * @return true if the exception handling context is in |
| * finally processing state. |
| */ |
| private boolean isActiveFinally() |
| { |
| return TryCatchFinallyState.FINALLY == this.tryCatchFinallyState; |
| } |
| |
| /** |
| * @return true if the exception handling context has a finally block. |
| */ |
| private boolean hasFinallyBlock() |
| { |
| return this.hasFinally; |
| } |
| |
| /** |
| * Marks this context as having a finally block. |
| * @param has_finally true if this context has a finally block, false otherwise. |
| */ |
| void setHasFinallyBlock(boolean has_finally) |
| { |
| this.hasFinally = has_finally; |
| } |
| } |