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