Refactored packagename to net.sourceforge.phpdt.internal.compiler.ast
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / TryStatement.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java
new file mode 100644 (file)
index 0000000..59a804d
--- /dev/null
@@ -0,0 +1,566 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package net.sourceforge.phpdt.internal.compiler.ast;
+
+import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
+import net.sourceforge.phpdt.internal.compiler.codegen.Label;
+import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FinallyFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.flow.InsideSubRoutineFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
+
+public class TryStatement extends Statement {
+       
+       public Block tryBlock;
+       public Block[] catchBlocks;
+       public Argument[] catchArguments;
+       public Block finallyBlock;
+       BlockScope scope;
+
+       public boolean subRoutineCannotReturn = true;
+       public UnconditionalFlowInfo subRoutineInits;
+       
+       // should rename into subRoutineComplete to be set to false by default
+
+       ReferenceBinding[] caughtExceptionTypes;
+       boolean tryBlockExit;
+       boolean[] catchExits;
+       public int[] preserveExceptionHandler;
+
+       Label subRoutineStartLabel;
+       public LocalVariableBinding anyExceptionVariable,
+               returnAddressVariable,
+               secretReturnValue;
+
+       public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
+       public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
+       public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
+
+       // for local variables table attributes
+       int preTryInitStateIndex = -1;
+       int mergedInitStateIndex = -1;
+
+       public FlowInfo analyseCode(
+               BlockScope currentScope,
+               FlowContext flowContext,
+               FlowInfo flowInfo) {
+
+               // Consider the try block and catch block so as to compute the intersection of initializations and      
+               // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
+               // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
+               // complete, then only keep this result for the rest of the analysis
+
+               // process the finally block (subroutine) - create a context for the subroutine
+
+               preTryInitStateIndex =
+                       currentScope.methodScope().recordInitializationStates(flowInfo);
+
+               if (anyExceptionVariable != null) {
+                       anyExceptionVariable.useFlag = LocalVariableBinding.USED;
+               }
+               if (returnAddressVariable != null) {
+                       returnAddressVariable.useFlag = LocalVariableBinding.USED;
+               }
+               InsideSubRoutineFlowContext insideSubContext;
+               FinallyFlowContext finallyContext;
+               UnconditionalFlowInfo subInfo;
+               if (subRoutineStartLabel == null) {
+                       // no finally block
+                       insideSubContext = null;
+                       finallyContext = null;
+                       subInfo = null;
+               } else {
+                       // analyse finally block first
+                       insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
+                       subInfo = 
+                               finallyBlock
+                                       .analyseCode(
+                                               currentScope,
+                                               finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
+                                               flowInfo.copy())
+                                       .unconditionalInits();
+                       if (subInfo.isReachable()) {
+                               subRoutineCannotReturn = false;
+                       }
+                       this.subRoutineInits = subInfo;
+               }
+               // process the try block in a context handling the local exceptions.
+               ExceptionHandlingFlowContext handlingContext =
+                       new ExceptionHandlingFlowContext(
+                               insideSubContext == null ? flowContext : insideSubContext,
+                               tryBlock,
+                               caughtExceptionTypes,
+                               scope,
+                               flowInfo.unconditionalInits());
+
+               FlowInfo tryInfo;
+               if (tryBlock.statements == null) {
+                       tryInfo = flowInfo;
+                       tryBlockExit = false;
+               } else {
+                       tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
+                       tryBlockExit = !tryInfo.isReachable();
+               }
+
+               // check unreachable catch blocks
+//             handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
+
+               // process the catch blocks - computing the minimal exit depth amongst try/catch
+               if (catchArguments != null) {
+                       int catchCount;
+                       catchExits = new boolean[catchCount = catchBlocks.length];
+                       for (int i = 0; i < catchCount; i++) {
+                               // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
+                               FlowInfo catchInfo =
+                                       flowInfo
+                                               .copy()
+                                               .unconditionalInits()
+                                               .addPotentialInitializationsFrom(
+                                                       handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
+                                               .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
+                                               .addPotentialInitializationsFrom(handlingContext.initsOnReturn);
+
+                               // catch var is always set
+                               catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
+                               /*
+                               "If we are about to consider an unchecked exception handler, potential inits may have occured inside
+                               the try block that need to be detected , e.g. 
+                               try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
+                               "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
+                               ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
+                               */
+                               // TODO: should only tag as unreachable if the catchblock cannot be reached
+                               //??? if (!handlingContext.initsOnException(caughtExceptionTypes[i]).isReachable()){
+                               if (tryBlock.statements == null) {
+                                       catchInfo.setReachMode(FlowInfo.UNREACHABLE);
+                               }
+                               catchInfo =
+                                       catchBlocks[i].analyseCode(
+                                               currentScope,
+                                               insideSubContext == null ? flowContext : insideSubContext,
+                                               catchInfo);
+                               catchExits[i] = !catchInfo.isReachable();
+                               tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
+                       }
+               }
+               if (subRoutineStartLabel == null) {
+                       mergedInitStateIndex =
+                               currentScope.methodScope().recordInitializationStates(tryInfo);
+                       return tryInfo;
+               }
+
+
+               // we also need to check potential multiple assignments of final variables inside the finally block
+               // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
+               finallyContext.complainOnRedundantFinalAssignments(
+                       tryInfo.isReachable() 
+                               ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
+                               : insideSubContext.initsOnReturn, 
+                       currentScope);
+               if (subInfo == FlowInfo.DEAD_END) {
+                       mergedInitStateIndex =
+                               currentScope.methodScope().recordInitializationStates(subInfo);
+                       return subInfo;
+               } else {
+                       FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
+                       mergedInitStateIndex =
+                               currentScope.methodScope().recordInitializationStates(mergedInfo);
+                       return mergedInfo;
+               }
+       }
+
+       public boolean cannotReturn() {
+
+               return subRoutineCannotReturn;
+       }
+
+       /**
+        * Try statement code generation
+        *
+        */
+//     public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+//
+//             if ((bits & IsReachableMASK) == 0) {
+//                     return;
+//             }
+//             if (tryBlock.isEmptyBlock()) {
+//                     if (subRoutineStartLabel != null) {
+//                             // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
+//                             finallyBlock.generateCode(scope, codeStream);
+//                     }
+//                     // May loose some local variable initializations : affecting the local variable attributes
+//                     if (mergedInitStateIndex != -1) {
+//                             codeStream.removeNotDefinitelyAssignedVariables(
+//                                     currentScope,
+//                                     mergedInitStateIndex);
+//                     }
+//                     // no local bytecode produced so no need for position remembering
+//                     return;
+//             }
+//             int pc = codeStream.position;
+//             Label endLabel = new Label(codeStream);
+//             boolean requiresNaturalJsr = false;
+//
+//             // preparing exception labels
+//             int maxCatches;
+//             ExceptionLabel[] exceptionLabels =
+//                     new ExceptionLabel[maxCatches =
+//                             catchArguments == null ? 0 : catchArguments.length];
+//             for (int i = 0; i < maxCatches; i++) {
+//                     boolean preserveCurrentHandler =
+//                             (preserveExceptionHandler[i
+//                                     / ExceptionHandlingFlowContext.BitCacheSize]
+//                                             & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+//                                     != 0;
+//                     if (preserveCurrentHandler) {
+//                             exceptionLabels[i] =
+//                                     new ExceptionLabel(
+//                                             codeStream,
+//                                             (ReferenceBinding) catchArguments[i].binding.type);
+//                     }
+//             }
+//             ExceptionLabel anyExceptionLabel = null;
+//             if (subRoutineStartLabel != null) {
+//                     subRoutineStartLabel.codeStream = codeStream;
+//                     anyExceptionLabel = new ExceptionLabel(codeStream, null);
+//             }
+//             // generate the try block
+//             tryBlock.generateCode(scope, codeStream);
+//             boolean tryBlockHasSomeCode = codeStream.position != pc;
+//             // flag telling if some bytecodes were issued inside the try block
+//
+//             // natural exit: only if necessary
+//             boolean nonReturningSubRoutine =
+//                     (subRoutineStartLabel != null) && subRoutineCannotReturn;
+//             if ((!tryBlockExit) && tryBlockHasSomeCode) {
+//                     int position = codeStream.position;
+//                     if (nonReturningSubRoutine) {
+//                             codeStream.goto_(subRoutineStartLabel);
+//                     } else {
+//                             requiresNaturalJsr = true;
+//                             codeStream.goto_(endLabel);
+//                     }
+//                     codeStream.updateLastRecordedEndPC(position);
+//                     //goto is tagged as part of the try block
+//             }
+//             // place end positions of user-defined exception labels
+//             if (tryBlockHasSomeCode) {
+//                     for (int i = 0; i < maxCatches; i++) {
+//                             boolean preserveCurrentHandler =
+//                                     (preserveExceptionHandler[i     / ExceptionHandlingFlowContext.BitCacheSize]
+//                                                     & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+//                                             != 0;
+//                             if (preserveCurrentHandler) {
+//                                     exceptionLabels[i].placeEnd();
+//                             }
+//                     }
+//                     /* generate sequence of handler, all starting by storing the TOS (exception
+//                     thrown) into their own catch variables, the one specified in the source
+//                     that must denote the handled exception.
+//                     */
+//                     if (catchArguments == null) {
+//                             if (anyExceptionLabel != null) {
+//                                     anyExceptionLabel.placeEnd();
+//                             }
+//                     } else {
+//                             for (int i = 0; i < maxCatches; i++) {
+//                                     boolean preserveCurrentHandler =
+//                                             (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
+//                                                             & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+//                                                     != 0;
+//                                     if (preserveCurrentHandler) {
+//                                             // May loose some local variable initializations : affecting the local variable attributes
+//                                             if (preTryInitStateIndex != -1) {
+//                                                     codeStream.removeNotDefinitelyAssignedVariables(
+//                                                             currentScope,
+//                                                             preTryInitStateIndex);
+//                                             }
+//                                             exceptionLabels[i].place();
+//                                             codeStream.incrStackSize(1);
+//                                             // optimizing the case where the exception variable is not actually used
+//                                             LocalVariableBinding catchVar;
+//                                             int varPC = codeStream.position;
+//                                             if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
+//                                                     codeStream.store(catchVar, false);
+//                                                     catchVar.recordInitializationStartPC(codeStream.position);
+//                                                     codeStream.addVisibleLocalVariable(catchVar);
+//                                             } else {
+//                                                     codeStream.pop();
+//                                             }
+//                                             codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
+//                                             // Keep track of the pcs at diverging point for computing the local attribute
+//                                             // since not passing the catchScope, the block generation will exitUserScope(catchScope)
+//                                             catchBlocks[i].generateCode(scope, codeStream);
+//                                     }
+//                                     if (i == maxCatches - 1) {
+//                                             if (anyExceptionLabel != null) {
+//                                                     anyExceptionLabel.placeEnd();
+//                                             }
+//                                             if (subRoutineStartLabel != null) {
+//                                                     if (!catchExits[i] && preserveCurrentHandler) {
+//                                                             requiresNaturalJsr = true;
+//                                                             codeStream.goto_(endLabel);
+//                                                     }
+//                                             }
+//                                     } else {
+//                                             if (!catchExits[i] && preserveCurrentHandler) {
+//                                                     if (nonReturningSubRoutine) {
+//                                                             codeStream.goto_(subRoutineStartLabel);
+//                                                     } else {
+//                                                             requiresNaturalJsr = true;
+//                                                             codeStream.goto_(endLabel);
+//                                                     }
+//                                             }
+//                                     }
+//                             }
+//                     }
+//                     // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
+//                     // inside catch blocks) will run the finally block
+//                     int finallySequenceStartPC = codeStream.position;
+//                     if (subRoutineStartLabel != null) {
+//                             // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
+//                             anyExceptionLabel.place();
+//
+//                             if (preTryInitStateIndex != -1) {
+//                                     // reset initialization state, as for a normal catch block
+//                                     codeStream.removeNotDefinitelyAssignedVariables(
+//                                             currentScope,
+//                                             preTryInitStateIndex);
+//                             }
+//
+//                             codeStream.incrStackSize(1);
+//                             if (nonReturningSubRoutine) {
+//                                     codeStream.pop();
+//                                     // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
+//                             } else {
+//                                     codeStream.store(anyExceptionVariable, false);
+//                                     codeStream.jsr(subRoutineStartLabel);
+//                                     codeStream.load(anyExceptionVariable);
+//                                     codeStream.athrow();
+//                             }
+//                     }
+//                     // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
+//                     endLabel.place();
+//                     if (subRoutineStartLabel != null) {
+//                             if (nonReturningSubRoutine) {
+//                                     requiresNaturalJsr = false;
+//                             }
+//                             Label veryEndLabel = new Label(codeStream);
+//                             if (requiresNaturalJsr) {
+//                                     codeStream.jsr(subRoutineStartLabel);
+//                                     codeStream.goto_(veryEndLabel);
+//                             }
+//                             subRoutineStartLabel.place();
+//                             if (!nonReturningSubRoutine) {
+//                                     codeStream.incrStackSize(1);
+//                                     codeStream.store(returnAddressVariable, false);
+//                             }
+//                             codeStream.recordPositionsFrom(
+//                                     finallySequenceStartPC,
+//                                     finallyBlock.sourceStart);
+//                             // entire sequence for finally is associated to finally block
+//                             finallyBlock.generateCode(scope, codeStream);
+//                             if (!nonReturningSubRoutine) {
+//                                     int position = codeStream.position;
+//                                     codeStream.ret(returnAddressVariable.resolvedPosition);
+//                                     codeStream.updateLastRecordedEndPC(position);
+//                                     // the ret bytecode is part of the subroutine
+//                             }
+//                             if (requiresNaturalJsr) {
+//                                     veryEndLabel.place();
+//                             }
+//                     }
+//             } else {
+//                     // try block had no effect, only generate the body of the finally block if any
+//                     if (subRoutineStartLabel != null) {
+//                             finallyBlock.generateCode(scope, codeStream);
+//                     }
+//             }
+//             // May loose some local variable initializations : affecting the local variable attributes
+//             if (mergedInitStateIndex != -1) {
+//                     codeStream.removeNotDefinitelyAssignedVariables(
+//                             currentScope,
+//                             mergedInitStateIndex);
+//                     codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
+//             }
+//             codeStream.recordPositionsFrom(pc, this.sourceStart);
+//     }
+
+       public void resetStateForCodeGeneration() {
+               if (this.subRoutineStartLabel != null) {
+                       this.subRoutineStartLabel.resetStateForCodeGeneration();
+               }
+       }       
+       public StringBuffer printStatement(int indent, StringBuffer output) {
+               printIndent(indent, output).append("try \n"); //$NON-NLS-1$
+               tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
+
+               //catches
+               if (catchBlocks != null)
+                       for (int i = 0; i < catchBlocks.length; i++) {
+                                       output.append('\n');
+                                       printIndent(indent, output).append("catch ("); //$NON-NLS-1$
+                                       catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
+                                       catchBlocks[i].printStatement(indent + 1, output);
+                       }
+               //finally
+               if (finallyBlock != null) {
+                       output.append('\n');
+                       printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
+                       finallyBlock.printStatement(indent + 1, output);
+               }
+
+               return output;
+       }
+       public void resolve(BlockScope upperScope) {
+
+               // special scope for secret locals optimization.        
+               this.scope = new BlockScope(upperScope);
+
+               BlockScope tryScope = new BlockScope(scope);
+               BlockScope finallyScope = null;
+               
+               if (finallyBlock != null
+                       && finallyBlock.statements != null) {
+
+                       finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
+
+                       // provision for returning and forcing the finally block to run
+                       MethodScope methodScope = scope.methodScope();
+
+                       // the type does not matter as long as it is not a base type
+                       this.returnAddressVariable =
+                               new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
+                       finallyScope.addLocalVariable(returnAddressVariable);
+                       this.returnAddressVariable.constant = NotAConstant; // not inlinable
+                       this.subRoutineStartLabel = new Label();
+
+                       this.anyExceptionVariable =
+                               new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
+                       finallyScope.addLocalVariable(this.anyExceptionVariable);
+                       this.anyExceptionVariable.constant = NotAConstant; // not inlinable
+
+                       if (!methodScope.isInsideInitializer()) {
+                               MethodBinding methodBinding =
+                                       ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
+                               if (methodBinding != null) {
+                                       TypeBinding methodReturnType = methodBinding.returnType;
+                                       if (methodReturnType.id != T_void) {
+                                               this.secretReturnValue =
+                                                       new LocalVariableBinding(
+                                                               SecretLocalDeclarationName,
+                                                               methodReturnType,
+                                                               AccDefault,
+                                                               false);
+                                               finallyScope.addLocalVariable(this.secretReturnValue);
+                                               this.secretReturnValue.constant = NotAConstant; // not inlinable
+                                       }
+                               }
+                       }
+                       finallyBlock.resolveUsing(finallyScope);
+                       // force the finally scope to have variable positions shifted after its try scope and catch ones
+                       finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
+                       finallyScope.shiftScopes[0] = tryScope;
+               }
+               this.tryBlock.resolveUsing(tryScope);
+
+               // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
+               if (this.catchBlocks != null) {
+                       int length = this.catchArguments.length;
+                       TypeBinding[] argumentTypes = new TypeBinding[length];
+                       for (int i = 0; i < length; i++) {
+                               BlockScope catchScope = new BlockScope(scope);
+                               if (finallyScope != null){
+                                       finallyScope.shiftScopes[i+1] = catchScope;
+                               }
+                               // side effect on catchScope in resolveForCatch(..)
+                               if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null)
+                                       return;
+                               catchBlocks[i].resolveUsing(catchScope);
+                       }
+
+                       // Verify that the catch clause are ordered in the right way:
+                       // more specialized first.
+                       this.caughtExceptionTypes = new ReferenceBinding[length];
+                       for (int i = 0; i < length; i++) {
+                               caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
+                               for (int j = 0; j < i; j++) {
+                                       if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
+                                               scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
+                                               // cannot return - since may still proceed if unreachable code is ignored (21203)
+                                       }
+                               }
+                       }
+               } else {
+                       caughtExceptionTypes = new ReferenceBinding[0];
+               }
+               
+               if (finallyScope != null){
+                       // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
+                       // the shifting is necessary to achieve no overlay in between the finally scope and its
+                       // sibling in term of local variable positions.
+                       this.scope.addSubscope(finallyScope);
+               }
+       }
+
+       public String toString(int tab) {
+               String s = tabString(tab);
+               //try
+               s = s + "try "; //$NON-NLS-1$
+               if (tryBlock == Block.None)
+                       s = s + "{}"; //$NON-NLS-1$
+               else
+                       s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
+
+               //catches
+               if (catchBlocks != null)
+                       for (int i = 0; i < catchBlocks.length; i++)
+                                       s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
+                                               +catchArguments[i].toString(0) + ") " //$NON-NLS-1$
+                                               +catchBlocks[i].toString(tab + 1);
+               //finally
+               if (finallyBlock != null) {
+                       if (finallyBlock == Block.None)
+                               s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
+                       else
+                                       s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
+                       finallyBlock.toString(tab + 1);
+               }
+
+               return s;
+       }
+
+       public void traverse(
+           ASTVisitor visitor,
+               BlockScope blockScope) {
+
+               if (visitor.visit(this, blockScope)) {
+                       tryBlock.traverse(visitor, scope);
+                       if (catchArguments != null) {
+                               for (int i = 0, max = catchBlocks.length; i < max; i++) {
+                                       catchArguments[i].traverse(visitor, scope);
+                                       catchBlocks[i].traverse(visitor, scope);
+                               }
+                       }
+                       if (finallyBlock != null)
+                               finallyBlock.traverse(visitor, scope);
+               }
+               visitor.endVisit(this, blockScope);
+       }
+}