added -J option to preserve unmodified files in preexisting jarfile
[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().unconditionalInits().discardNullRelatedInitializations())
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                                 LocalVariableBinding catchArg = catchArguments[i].binding;
132                                 FlowContext catchContext = insideSubContext == null ? flowContext : insideSubContext;
133                                 catchInfo.markAsDefinitelyAssigned(catchArg);
134                                 catchInfo.markAsDefinitelyNonNull(catchArg);
135                                 /*
136                                 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
137                                 the try block that need to be detected , e.g. 
138                                 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
139                                 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
140                                 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
141                                 */
142                                 if (tryBlock.statements == null) {
143                                         catchInfo.setReachMode(FlowInfo.UNREACHABLE);
144                                 }
145                                 catchInfo =
146                                         catchBlocks[i].analyseCode(
147                                                 currentScope,
148                                                 catchContext,
149                                                 catchInfo);
150                                 catchExits[i] = !catchInfo.isReachable();
151                                 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
152                         }
153                 }
154                 if (subRoutineStartLabel == null) {
155                         mergedInitStateIndex =
156                                 currentScope.methodScope().recordInitializationStates(tryInfo);
157                         return tryInfo;
158                 }
159
160
161                 // we also need to check potential multiple assignments of final variables inside the finally block
162                 // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
163                 finallyContext.complainOnDeferredChecks(
164                         tryInfo.isReachable() 
165                                 ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
166                                 : insideSubContext.initsOnReturn, 
167                         currentScope);
168                 if (subInfo == FlowInfo.DEAD_END) {
169                         mergedInitStateIndex =
170                                 currentScope.methodScope().recordInitializationStates(subInfo);
171                         return subInfo;
172                 } else {
173                         FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
174                         mergedInitStateIndex =
175                                 currentScope.methodScope().recordInitializationStates(mergedInfo);
176                         return mergedInfo;
177                 }
178         }
179
180         public boolean isSubRoutineEscaping() {
181
182                 return isSubRoutineEscaping;
183         }
184
185         /**
186          * Try statement code generation with or without jsr bytecode use
187          *      post 1.5 target level, cannot use jsr bytecode, must instead inline finally block
188          * returnAddress is only allocated if jsr is allowed
189          */
190         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
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                                 for (int i = 0; i < maxCatches; i++) {
265                                         // May loose some local variable initializations : affecting the local variable attributes
266                                         if (preTryInitStateIndex != -1) {
267                                                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
268                                         }
269                                         exceptionLabels[i].place();
270                                         codeStream.incrStackSize(1);
271                                         // optimizing the case where the exception variable is not actually used
272                                         LocalVariableBinding catchVar;
273                                         int varPC = codeStream.position;
274                                         if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
275                                                 codeStream.store(catchVar, false);
276                                                 catchVar.recordInitializationStartPC(codeStream.position);
277                                                 codeStream.addVisibleLocalVariable(catchVar);
278                                         } else {
279                                                 codeStream.pop();
280                                         }
281                                         codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
282                                         // Keep track of the pcs at diverging point for computing the local attribute
283                                         // since not passing the catchScope, the block generation will exitUserScope(catchScope)
284                                         catchBlocks[i].generateCode(scope, codeStream);
285                                         if (!catchExits[i]) {
286                                                 switch(finallyMode) {
287                                                         case FINALLY_SUBROUTINE :
288                                                         case FINALLY_MUST_BE_INLINED :
289                                                                 requiresNaturalExit = true;
290                                                                 // fall through
291                                                         case NO_FINALLY :
292                                                                 codeStream.goto_(naturalExitLabel);
293                                                                 break;
294                                                         case FINALLY_DOES_NOT_COMPLETE :
295                                                                 codeStream.goto_(subRoutineStartLabel);
296                                                                 break;
297                                                 }
298                                         }
299                                 }
300                         }
301                         this.exitAnyExceptionHandler();
302                         // extra handler for trailing natural exit (will be fixed up later on when natural exit is generated below)
303                         ExceptionLabel naturalExitExceptionHandler = 
304                                 finallyMode == FINALLY_SUBROUTINE && requiresNaturalExit ? new ExceptionLabel(codeStream, null) : null;
305
306                         // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
307                         // inside catch blocks) will run the finally block
308                         int finallySequenceStartPC = codeStream.position;
309                         if (subRoutineStartLabel != null) {
310                                 this.placeAllAnyExceptionHandlers();
311                                 if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place();
312                                 
313                                 if (preTryInitStateIndex != -1) {
314                                         // reset initialization state, as for a normal catch block
315                                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
316                                 }
317
318                                 codeStream.incrStackSize(1);
319                                 switch(finallyMode) {
320                                         case FINALLY_SUBROUTINE :
321                                                 codeStream.store(anyExceptionVariable, false);
322                                                 codeStream.jsr(subRoutineStartLabel);
323                                                 codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
324                                                 int position = codeStream.position;                                             
325                                                 codeStream.load(anyExceptionVariable);
326                                                 codeStream.athrow();
327                                                 codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
328                                                 subRoutineStartLabel.place();
329                                                 codeStream.incrStackSize(1);
330                                                 position = codeStream.position; 
331                                                 codeStream.store(returnAddressVariable, false);
332                                                 codeStream.recordPositionsFrom(position, finallyBlock.sourceStart);
333                                                 finallyBlock.generateCode(scope, codeStream);
334                                                 position = codeStream.position;
335                                                 codeStream.ret(returnAddressVariable.resolvedPosition);
336 //                                              codeStream.updateLastRecordedEndPC(position);
337                                                 codeStream.recordPositionsFrom(
338                                                         position,
339                                                         finallyBlock.sourceEnd);
340                                                 // the ret bytecode is part of the subroutine
341                                                 break;
342                                         case FINALLY_MUST_BE_INLINED :
343                                                 codeStream.store(anyExceptionVariable, false);
344                                                 codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
345                                                 this.finallyBlock.generateCode(currentScope, codeStream);
346                                                 position = codeStream.position;
347                                                 codeStream.load(anyExceptionVariable);
348                                                 codeStream.athrow();
349                                                 subRoutineStartLabel.place();
350                                                 codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
351                                                 break;
352                                         case FINALLY_DOES_NOT_COMPLETE :
353                                                 codeStream.pop();
354                                                 subRoutineStartLabel.place();
355                                                 codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
356                                                 finallyBlock.generateCode(scope, codeStream);
357                                                 break;
358                                 }
359                                 // will naturally fall into subsequent code after subroutine invocation
360                                 naturalExitLabel.place();
361                                 if (requiresNaturalExit) {
362                                         switch(finallyMode) {
363                                                 case FINALLY_SUBROUTINE :
364                                                         int position = codeStream.position;
365                                                         // fix up natural exit handler
366                                                         naturalExitExceptionHandler.placeStart();
367                                                         codeStream.jsr(subRoutineStartLabel);
368                                                         naturalExitExceptionHandler.placeEnd();
369                                                         codeStream.recordPositionsFrom(
370                                                                 position,
371                                                                 finallyBlock.sourceEnd);        
372                                                         break;
373                                                 case FINALLY_MUST_BE_INLINED :
374                                                         // May loose some local variable initializations : affecting the local variable attributes
375                                                         // needed since any exception handler got inlined subroutine
376                                                         if (preTryInitStateIndex != -1) {
377                                                                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
378                                                         }
379                                                         // entire sequence for finally is associated to finally block
380                                                         finallyBlock.generateCode(scope, codeStream);
381                                                         break;
382                                                 case FINALLY_DOES_NOT_COMPLETE :
383                                                         break;
384                                         }
385                                 }
386                         } else {
387                                 // no subroutine, simply position end label (natural exit == end)
388                                 naturalExitLabel.place();
389                         }
390                 } else {
391                         // try block had no effect, only generate the body of the finally block if any
392                         if (subRoutineStartLabel != null) {
393                                 finallyBlock.generateCode(scope, codeStream);
394                         }
395                 }
396                 // May loose some local variable initializations : affecting the local variable attributes
397                 if (mergedInitStateIndex != -1) {
398                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
399                         codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
400                 }
401                 codeStream.recordPositionsFrom(pc, this.sourceStart);
402         }
403
404         /* (non-Javadoc)
405          * @see org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement#generateSubRoutineInvocation(org.eclipse.jdt.internal.compiler.lookup.BlockScope, org.eclipse.jdt.internal.compiler.codegen.CodeStream)
406          */
407         public void generateSubRoutineInvocation(
408                         BlockScope currentScope,
409                         CodeStream codeStream) {
410         
411                 if (this.isSubRoutineEscaping) {
412                                 codeStream.goto_(this.subRoutineStartLabel);
413                 } else {
414                         if (currentScope.environment().options.inlineJsrBytecode) { 
415                                 // cannot use jsr bytecode, then simply inline the subroutine
416                                 this.finallyBlock.generateCode(currentScope, codeStream);
417                         } else {
418                                 // classic subroutine invocation, distinguish case of non-returning subroutine
419                                 codeStream.jsr(this.subRoutineStartLabel);
420                         }
421                 }
422         }
423
424         public StringBuffer printStatement(int indent, StringBuffer output) {
425                 printIndent(indent, output).append("try \n"); //$NON-NLS-1$
426                 tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
427
428                 //catches
429                 if (catchBlocks != null)
430                         for (int i = 0; i < catchBlocks.length; i++) {
431                                         output.append('\n');
432                                         printIndent(indent, output).append("catch ("); //$NON-NLS-1$
433                                         catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
434                                         catchBlocks[i].printStatement(indent + 1, output);
435                         }
436                 //finally
437                 if (finallyBlock != null) {
438                         output.append('\n');
439                         printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
440                         finallyBlock.printStatement(indent + 1, output);
441                 }
442
443                 return output;
444         }
445
446         public void resolve(BlockScope upperScope) {
447
448                 // special scope for secret locals optimization.        
449                 this.scope = new BlockScope(upperScope);
450
451                 BlockScope tryScope = new BlockScope(scope);
452                 BlockScope finallyScope = null;
453                 
454                 if (finallyBlock != null) {
455                         if (finallyBlock.isEmptyBlock()) {
456                                 if ((finallyBlock.bits & UndocumentedEmptyBlockMASK) != 0) {
457                                         scope.problemReporter().undocumentedEmptyBlock(finallyBlock.sourceStart, finallyBlock.sourceEnd);
458                                 }
459                         } else {
460                                 finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
461         
462                                 // provision for returning and forcing the finally block to run
463                                 MethodScope methodScope = scope.methodScope();
464         
465                                 // the type does not matter as long as it is not a base type
466                                 if (!upperScope.environment().options.inlineJsrBytecode) {
467                                         this.returnAddressVariable =
468                                                 new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
469                                         finallyScope.addLocalVariable(returnAddressVariable);
470                                         this.returnAddressVariable.setConstant(NotAConstant); // not inlinable
471                                 }
472                                 this.subRoutineStartLabel = new Label();
473         
474                                 this.anyExceptionVariable =
475                                         new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
476                                 finallyScope.addLocalVariable(this.anyExceptionVariable);
477                                 this.anyExceptionVariable.setConstant(NotAConstant); // not inlinable
478         
479                                 if (!methodScope.isInsideInitializer()) {
480                                         MethodBinding methodBinding =
481                                                 ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
482                                         if (methodBinding != null) {
483                                                 TypeBinding methodReturnType = methodBinding.returnType;
484                                                 if (methodReturnType.id != T_void) {
485                                                         this.secretReturnValue =
486                                                                 new LocalVariableBinding(
487                                                                         SecretLocalDeclarationName,
488                                                                         methodReturnType,
489                                                                         AccDefault,
490                                                                         false);
491                                                         finallyScope.addLocalVariable(this.secretReturnValue);
492                                                         this.secretReturnValue.setConstant(NotAConstant); // not inlinable
493                                                 }
494                                         }
495                                 }
496                                 finallyBlock.resolveUsing(finallyScope);
497                                 // force the finally scope to have variable positions shifted after its try scope and catch ones
498                                 finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
499                                 finallyScope.shiftScopes[0] = tryScope;
500                         }
501                 }
502                 this.tryBlock.resolveUsing(tryScope);
503
504                 // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
505                 if (this.catchBlocks != null) {
506                         int length = this.catchArguments.length;
507                         TypeBinding[] argumentTypes = new TypeBinding[length];
508                         boolean catchHasError = false;
509                         for (int i = 0; i < length; i++) {
510                                 BlockScope catchScope = new BlockScope(scope);
511                                 if (finallyScope != null){
512                                         finallyScope.shiftScopes[i+1] = catchScope;
513                                 }
514                                 // side effect on catchScope in resolveForCatch(..)
515                                 if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null) {
516                                         catchHasError = true;
517                                 }
518                                 catchBlocks[i].resolveUsing(catchScope);
519                         }
520                         if (catchHasError) {
521                                 return;
522                         }
523                         // Verify that the catch clause are ordered in the right way:
524                         // more specialized first.
525                         this.caughtExceptionTypes = new ReferenceBinding[length];
526                         for (int i = 0; i < length; i++) {
527                                 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
528                                 for (int j = 0; j < i; j++) {
529                                         if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
530                                                 scope.problemReporter().wrongSequenceOfExceptionTypesError(this, caughtExceptionTypes[i], i, argumentTypes[j]);
531                                         }
532                                 }
533                         }
534                 } else {
535                         caughtExceptionTypes = new ReferenceBinding[0];
536                 }
537                 
538                 if (finallyScope != null){
539                         // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
540                         // the shifting is necessary to achieve no overlay in between the finally scope and its
541                         // sibling in term of local variable positions.
542                         this.scope.addSubscope(finallyScope);
543                 }
544         }
545
546         public void traverse(
547                 ASTVisitor visitor,
548                 BlockScope blockScope) {
549
550                 if (visitor.visit(this, blockScope)) {
551                         tryBlock.traverse(visitor, scope);
552                         if (catchArguments != null) {
553                                 for (int i = 0, max = catchBlocks.length; i < max; i++) {
554                                         catchArguments[i].traverse(visitor, scope);
555                                         catchBlocks[i].traverse(visitor, scope);
556                                 }
557                         }
558                         if (finallyBlock != null)
559                                 finallyBlock.traverse(visitor, scope);
560                 }
561                 visitor.endVisit(this, blockScope);
562         }
563 }