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