8eca4751942af4ce69cf2ba2c91e31f3808fe8a2
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / internal / compiler / ast / TryStatement.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpeclipse.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
15 import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FinallyFlowContext;
17 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
18 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
19 import net.sourceforge.phpdt.internal.compiler.flow.InsideSubRoutineFlowContext;
20 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
21 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
24 import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
25 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
26 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
27
28 public class TryStatement extends Statement {
29         
30         public Block tryBlock;
31         public Block[] catchBlocks;
32         public Argument[] catchArguments;
33         public Block finallyBlock;
34         BlockScope scope;
35
36         public boolean subRoutineCannotReturn = true;
37         public UnconditionalFlowInfo subRoutineInits;
38         
39         // should rename into subRoutineComplete to be set to false by default
40
41         ReferenceBinding[] caughtExceptionTypes;
42         boolean tryBlockExit;
43         boolean[] catchExits;
44         public int[] preserveExceptionHandler;
45
46         Label subRoutineStartLabel;
47         public LocalVariableBinding anyExceptionVariable,
48                 returnAddressVariable,
49                 secretReturnValue;
50
51         public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
52         public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
53         public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
54
55         // for local variables table attributes
56         int preTryInitStateIndex = -1;
57         int mergedInitStateIndex = -1;
58
59         public FlowInfo analyseCode(
60                 BlockScope currentScope,
61                 FlowContext flowContext,
62                 FlowInfo flowInfo) {
63
64                 // Consider the try block and catch block so as to compute the intersection of initializations and      
65                 // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
66                 // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
67                 // complete, then only keep this result for the rest of the analysis
68
69                 // process the finally block (subroutine) - create a context for the subroutine
70
71                 preTryInitStateIndex =
72                         currentScope.methodScope().recordInitializationStates(flowInfo);
73
74                 if (anyExceptionVariable != null) {
75                         anyExceptionVariable.useFlag = LocalVariableBinding.USED;
76                 }
77                 if (returnAddressVariable != null) {
78                         returnAddressVariable.useFlag = LocalVariableBinding.USED;
79                 }
80                 InsideSubRoutineFlowContext insideSubContext;
81                 FinallyFlowContext finallyContext;
82                 UnconditionalFlowInfo subInfo;
83                 if (subRoutineStartLabel == null) {
84                         // no finally block
85                         insideSubContext = null;
86                         finallyContext = null;
87                         subInfo = null;
88                 } else {
89                         // analyse finally block first
90                         insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
91                         subInfo = 
92                                 finallyBlock
93                                         .analyseCode(
94                                                 currentScope,
95                                                 finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
96                                                 flowInfo.copy())
97                                         .unconditionalInits();
98                         if (subInfo.isReachable()) {
99                                 subRoutineCannotReturn = false;
100                         }
101                         this.subRoutineInits = subInfo;
102                 }
103                 // process the try block in a context handling the local exceptions.
104                 ExceptionHandlingFlowContext handlingContext =
105                         new ExceptionHandlingFlowContext(
106                                 insideSubContext == null ? flowContext : insideSubContext,
107                                 tryBlock,
108                                 caughtExceptionTypes,
109                                 scope,
110                                 flowInfo.unconditionalInits());
111
112                 FlowInfo tryInfo;
113                 if (tryBlock.statements == null) {
114                         tryInfo = flowInfo;
115                         tryBlockExit = false;
116                 } else {
117                         tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
118                         tryBlockExit = !tryInfo.isReachable();
119                 }
120
121                 // check unreachable catch blocks
122                 handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
123
124                 // process the catch blocks - computing the minimal exit depth amongst try/catch
125                 if (catchArguments != null) {
126                         int catchCount;
127                         catchExits = new boolean[catchCount = catchBlocks.length];
128                         for (int i = 0; i < catchCount; i++) {
129                                 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
130                                 FlowInfo catchInfo =
131                                         flowInfo
132                                                 .copy()
133                                                 .unconditionalInits()
134                                                 .addPotentialInitializationsFrom(
135                                                         handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
136                                                 .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
137                                                 .addPotentialInitializationsFrom(handlingContext.initsOnReturn);
138
139                                 // catch var is always set
140                                 catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
141                                 /*
142                                 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
143                                 the try block that need to be detected , e.g. 
144                                 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
145                                 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
146                                 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
147                                 */
148                                 // TODO: should only tag as unreachable if the catchblock cannot be reached
149                                 //??? if (!handlingContext.initsOnException(caughtExceptionTypes[i]).isReachable()){
150                                 if (tryBlock.statements == null) {
151                                         catchInfo.setReachMode(FlowInfo.UNREACHABLE);
152                                 }
153                                 catchInfo =
154                                         catchBlocks[i].analyseCode(
155                                                 currentScope,
156                                                 insideSubContext == null ? flowContext : insideSubContext,
157                                                 catchInfo);
158                                 catchExits[i] = !catchInfo.isReachable();
159                                 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
160                         }
161                 }
162                 if (subRoutineStartLabel == null) {
163                         mergedInitStateIndex =
164                                 currentScope.methodScope().recordInitializationStates(tryInfo);
165                         return tryInfo;
166                 }
167
168
169                 // we also need to check potential multiple assignments of final variables inside the finally block
170                 // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
171                 finallyContext.complainOnRedundantFinalAssignments(
172                         tryInfo.isReachable() 
173                                 ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
174                                 : insideSubContext.initsOnReturn, 
175                         currentScope);
176                 if (subInfo == FlowInfo.DEAD_END) {
177                         mergedInitStateIndex =
178                                 currentScope.methodScope().recordInitializationStates(subInfo);
179                         return subInfo;
180                 } else {
181                         FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
182                         mergedInitStateIndex =
183                                 currentScope.methodScope().recordInitializationStates(mergedInfo);
184                         return mergedInfo;
185                 }
186         }
187
188         public boolean cannotReturn() {
189
190                 return subRoutineCannotReturn;
191         }
192
193         /**
194          * Try statement code generation
195          *
196          */
197 //      public void generateCode(BlockScope currentScope, CodeStream codeStream) {
198 //
199 //              if ((bits & IsReachableMASK) == 0) {
200 //                      return;
201 //              }
202 //              if (tryBlock.isEmptyBlock()) {
203 //                      if (subRoutineStartLabel != null) {
204 //                              // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
205 //                              finallyBlock.generateCode(scope, codeStream);
206 //                      }
207 //                      // May loose some local variable initializations : affecting the local variable attributes
208 //                      if (mergedInitStateIndex != -1) {
209 //                              codeStream.removeNotDefinitelyAssignedVariables(
210 //                                      currentScope,
211 //                                      mergedInitStateIndex);
212 //                      }
213 //                      // no local bytecode produced so no need for position remembering
214 //                      return;
215 //              }
216 //              int pc = codeStream.position;
217 //              Label endLabel = new Label(codeStream);
218 //              boolean requiresNaturalJsr = false;
219 //
220 //              // preparing exception labels
221 //              int maxCatches;
222 //              ExceptionLabel[] exceptionLabels =
223 //                      new ExceptionLabel[maxCatches =
224 //                              catchArguments == null ? 0 : catchArguments.length];
225 //              for (int i = 0; i < maxCatches; i++) {
226 //                      boolean preserveCurrentHandler =
227 //                              (preserveExceptionHandler[i
228 //                                      / ExceptionHandlingFlowContext.BitCacheSize]
229 //                                              & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
230 //                                      != 0;
231 //                      if (preserveCurrentHandler) {
232 //                              exceptionLabels[i] =
233 //                                      new ExceptionLabel(
234 //                                              codeStream,
235 //                                              (ReferenceBinding) catchArguments[i].binding.type);
236 //                      }
237 //              }
238 //              ExceptionLabel anyExceptionLabel = null;
239 //              if (subRoutineStartLabel != null) {
240 //                      subRoutineStartLabel.codeStream = codeStream;
241 //                      anyExceptionLabel = new ExceptionLabel(codeStream, null);
242 //              }
243 //              // generate the try block
244 //              tryBlock.generateCode(scope, codeStream);
245 //              boolean tryBlockHasSomeCode = codeStream.position != pc;
246 //              // flag telling if some bytecodes were issued inside the try block
247 //
248 //              // natural exit: only if necessary
249 //              boolean nonReturningSubRoutine =
250 //                      (subRoutineStartLabel != null) && subRoutineCannotReturn;
251 //              if ((!tryBlockExit) && tryBlockHasSomeCode) {
252 //                      int position = codeStream.position;
253 //                      if (nonReturningSubRoutine) {
254 //                              codeStream.goto_(subRoutineStartLabel);
255 //                      } else {
256 //                              requiresNaturalJsr = true;
257 //                              codeStream.goto_(endLabel);
258 //                      }
259 //                      codeStream.updateLastRecordedEndPC(position);
260 //                      //goto is tagged as part of the try block
261 //              }
262 //              // place end positions of user-defined exception labels
263 //              if (tryBlockHasSomeCode) {
264 //                      for (int i = 0; i < maxCatches; i++) {
265 //                              boolean preserveCurrentHandler =
266 //                                      (preserveExceptionHandler[i     / ExceptionHandlingFlowContext.BitCacheSize]
267 //                                                      & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
268 //                                              != 0;
269 //                              if (preserveCurrentHandler) {
270 //                                      exceptionLabels[i].placeEnd();
271 //                              }
272 //                      }
273 //                      /* generate sequence of handler, all starting by storing the TOS (exception
274 //                      thrown) into their own catch variables, the one specified in the source
275 //                      that must denote the handled exception.
276 //                      */
277 //                      if (catchArguments == null) {
278 //                              if (anyExceptionLabel != null) {
279 //                                      anyExceptionLabel.placeEnd();
280 //                              }
281 //                      } else {
282 //                              for (int i = 0; i < maxCatches; i++) {
283 //                                      boolean preserveCurrentHandler =
284 //                                              (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
285 //                                                              & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
286 //                                                      != 0;
287 //                                      if (preserveCurrentHandler) {
288 //                                              // May loose some local variable initializations : affecting the local variable attributes
289 //                                              if (preTryInitStateIndex != -1) {
290 //                                                      codeStream.removeNotDefinitelyAssignedVariables(
291 //                                                              currentScope,
292 //                                                              preTryInitStateIndex);
293 //                                              }
294 //                                              exceptionLabels[i].place();
295 //                                              codeStream.incrStackSize(1);
296 //                                              // optimizing the case where the exception variable is not actually used
297 //                                              LocalVariableBinding catchVar;
298 //                                              int varPC = codeStream.position;
299 //                                              if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
300 //                                                      codeStream.store(catchVar, false);
301 //                                                      catchVar.recordInitializationStartPC(codeStream.position);
302 //                                                      codeStream.addVisibleLocalVariable(catchVar);
303 //                                              } else {
304 //                                                      codeStream.pop();
305 //                                              }
306 //                                              codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
307 //                                              // Keep track of the pcs at diverging point for computing the local attribute
308 //                                              // since not passing the catchScope, the block generation will exitUserScope(catchScope)
309 //                                              catchBlocks[i].generateCode(scope, codeStream);
310 //                                      }
311 //                                      if (i == maxCatches - 1) {
312 //                                              if (anyExceptionLabel != null) {
313 //                                                      anyExceptionLabel.placeEnd();
314 //                                              }
315 //                                              if (subRoutineStartLabel != null) {
316 //                                                      if (!catchExits[i] && preserveCurrentHandler) {
317 //                                                              requiresNaturalJsr = true;
318 //                                                              codeStream.goto_(endLabel);
319 //                                                      }
320 //                                              }
321 //                                      } else {
322 //                                              if (!catchExits[i] && preserveCurrentHandler) {
323 //                                                      if (nonReturningSubRoutine) {
324 //                                                              codeStream.goto_(subRoutineStartLabel);
325 //                                                      } else {
326 //                                                              requiresNaturalJsr = true;
327 //                                                              codeStream.goto_(endLabel);
328 //                                                      }
329 //                                              }
330 //                                      }
331 //                              }
332 //                      }
333 //                      // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
334 //                      // inside catch blocks) will run the finally block
335 //                      int finallySequenceStartPC = codeStream.position;
336 //                      if (subRoutineStartLabel != null) {
337 //                              // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
338 //                              anyExceptionLabel.place();
339 //
340 //                              if (preTryInitStateIndex != -1) {
341 //                                      // reset initialization state, as for a normal catch block
342 //                                      codeStream.removeNotDefinitelyAssignedVariables(
343 //                                              currentScope,
344 //                                              preTryInitStateIndex);
345 //                              }
346 //
347 //                              codeStream.incrStackSize(1);
348 //                              if (nonReturningSubRoutine) {
349 //                                      codeStream.pop();
350 //                                      // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
351 //                              } else {
352 //                                      codeStream.store(anyExceptionVariable, false);
353 //                                      codeStream.jsr(subRoutineStartLabel);
354 //                                      codeStream.load(anyExceptionVariable);
355 //                                      codeStream.athrow();
356 //                              }
357 //                      }
358 //                      // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
359 //                      endLabel.place();
360 //                      if (subRoutineStartLabel != null) {
361 //                              if (nonReturningSubRoutine) {
362 //                                      requiresNaturalJsr = false;
363 //                              }
364 //                              Label veryEndLabel = new Label(codeStream);
365 //                              if (requiresNaturalJsr) {
366 //                                      codeStream.jsr(subRoutineStartLabel);
367 //                                      codeStream.goto_(veryEndLabel);
368 //                              }
369 //                              subRoutineStartLabel.place();
370 //                              if (!nonReturningSubRoutine) {
371 //                                      codeStream.incrStackSize(1);
372 //                                      codeStream.store(returnAddressVariable, false);
373 //                              }
374 //                              codeStream.recordPositionsFrom(
375 //                                      finallySequenceStartPC,
376 //                                      finallyBlock.sourceStart);
377 //                              // entire sequence for finally is associated to finally block
378 //                              finallyBlock.generateCode(scope, codeStream);
379 //                              if (!nonReturningSubRoutine) {
380 //                                      int position = codeStream.position;
381 //                                      codeStream.ret(returnAddressVariable.resolvedPosition);
382 //                                      codeStream.updateLastRecordedEndPC(position);
383 //                                      // the ret bytecode is part of the subroutine
384 //                              }
385 //                              if (requiresNaturalJsr) {
386 //                                      veryEndLabel.place();
387 //                              }
388 //                      }
389 //              } else {
390 //                      // try block had no effect, only generate the body of the finally block if any
391 //                      if (subRoutineStartLabel != null) {
392 //                              finallyBlock.generateCode(scope, codeStream);
393 //                      }
394 //              }
395 //              // May loose some local variable initializations : affecting the local variable attributes
396 //              if (mergedInitStateIndex != -1) {
397 //                      codeStream.removeNotDefinitelyAssignedVariables(
398 //                              currentScope,
399 //                              mergedInitStateIndex);
400 //                      codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
401 //              }
402 //              codeStream.recordPositionsFrom(pc, this.sourceStart);
403 //      }
404
405         public void resetStateForCodeGeneration() {
406                 if (this.subRoutineStartLabel != null) {
407                         this.subRoutineStartLabel.resetStateForCodeGeneration();
408                 }
409         }       
410
411         public void resolve(BlockScope upperScope) {
412
413                 // special scope for secret locals optimization.        
414                 this.scope = new BlockScope(upperScope);
415
416                 BlockScope tryScope = new BlockScope(scope);
417                 BlockScope finallyScope = null;
418                 
419                 if (finallyBlock != null
420                         && finallyBlock.statements != null) {
421
422                         finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
423
424                         // provision for returning and forcing the finally block to run
425                         MethodScope methodScope = scope.methodScope();
426
427                         // the type does not matter as long as it is not a base type
428                         this.returnAddressVariable =
429                                 new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
430                         finallyScope.addLocalVariable(returnAddressVariable);
431                         this.returnAddressVariable.constant = NotAConstant; // not inlinable
432                         this.subRoutineStartLabel = new Label();
433
434                         this.anyExceptionVariable =
435                                 new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
436                         finallyScope.addLocalVariable(this.anyExceptionVariable);
437                         this.anyExceptionVariable.constant = NotAConstant; // not inlinable
438
439                         if (!methodScope.isInsideInitializer()) {
440                                 MethodBinding methodBinding =
441                                         ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
442                                 if (methodBinding != null) {
443                                         TypeBinding methodReturnType = methodBinding.returnType;
444                                         if (methodReturnType.id != T_void) {
445                                                 this.secretReturnValue =
446                                                         new LocalVariableBinding(
447                                                                 SecretLocalDeclarationName,
448                                                                 methodReturnType,
449                                                                 AccDefault,
450                                                                 false);
451                                                 finallyScope.addLocalVariable(this.secretReturnValue);
452                                                 this.secretReturnValue.constant = NotAConstant; // not inlinable
453                                         }
454                                 }
455                         }
456                         finallyBlock.resolveUsing(finallyScope);
457                         // force the finally scope to have variable positions shifted after its try scope and catch ones
458                         finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
459                         finallyScope.shiftScopes[0] = tryScope;
460                 }
461                 this.tryBlock.resolveUsing(tryScope);
462
463                 // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
464                 if (this.catchBlocks != null) {
465                         int length = this.catchArguments.length;
466                         TypeBinding[] argumentTypes = new TypeBinding[length];
467                         for (int i = 0; i < length; i++) {
468                                 BlockScope catchScope = new BlockScope(scope);
469                                 if (finallyScope != null){
470                                         finallyScope.shiftScopes[i+1] = catchScope;
471                                 }
472                                 // side effect on catchScope in resolveForCatch(..)
473                                 if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null)
474                                         return;
475                                 catchBlocks[i].resolveUsing(catchScope);
476                         }
477
478                         // Verify that the catch clause are ordered in the right way:
479                         // more specialized first.
480                         this.caughtExceptionTypes = new ReferenceBinding[length];
481                         for (int i = 0; i < length; i++) {
482                                 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
483                                 for (int j = 0; j < i; j++) {
484                                         if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
485                                                 scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
486                                                 // cannot return - since may still proceed if unreachable code is ignored (21203)
487                                         }
488                                 }
489                         }
490                 } else {
491                         caughtExceptionTypes = new ReferenceBinding[0];
492                 }
493                 
494                 if (finallyScope != null){
495                         // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
496                         // the shifting is necessary to achieve no overlay in between the finally scope and its
497                         // sibling in term of local variable positions.
498                         this.scope.addSubscope(finallyScope);
499                 }
500         }
501
502         public String toString(int tab) {
503                 String s = tabString(tab);
504                 //try
505                 s = s + "try "; //$NON-NLS-1$
506                 if (tryBlock == Block.None)
507                         s = s + "{}"; //$NON-NLS-1$
508                 else
509                         s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
510
511                 //catches
512                 if (catchBlocks != null)
513                         for (int i = 0; i < catchBlocks.length; i++)
514                                         s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
515                                                 +catchArguments[i].toString(0) + ") " //$NON-NLS-1$
516                                                 +catchBlocks[i].toString(tab + 1);
517                 //finally
518                 if (finallyBlock != null) {
519                         if (finallyBlock == Block.None)
520                                 s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
521                         else
522                                         s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
523                         finallyBlock.toString(tab + 1);
524                 }
525
526                 return s;
527         }
528
529         public void traverse(
530                 IAbstractSyntaxTreeVisitor visitor,
531                 BlockScope blockScope) {
532
533                 if (visitor.visit(this, blockScope)) {
534                         tryBlock.traverse(visitor, scope);
535                         if (catchArguments != null) {
536                                 for (int i = 0, max = catchBlocks.length; i < max; i++) {
537                                         catchArguments[i].traverse(visitor, scope);
538                                         catchBlocks[i].traverse(visitor, scope);
539                                 }
540                         }
541                         if (finallyBlock != null)
542                                 finallyBlock.traverse(visitor, scope);
543                 }
544                 visitor.endVisit(this, blockScope);
545         }
546 }