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 
analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)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 
isSubRoutineEscaping()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 	 */
generateCode(BlockScope currentScope, CodeStream codeStream)189 	public void generateCode(BlockScope currentScope, CodeStream codeStream) {
190 		if ((bits & IsReachableMASK) == 0) {
191 			return;
192 		}
193 		// in case the labels needs to be reinitialized
194 		// when the code generation is restarted in wide mode
195 		if (this.anyExceptionLabelsCount > 0) {
196 			this.anyExceptionLabels = NO_EXCEPTION_HANDLER;
197 			this.anyExceptionLabelsCount = 0;
198 		}
199 		int pc = codeStream.position;
200 		final int NO_FINALLY = 0;									// no finally block
201 		final int FINALLY_SUBROUTINE = 1; 					// finally is generated as a subroutine (using jsr/ret bytecodes)
202 		final int FINALLY_DOES_NOT_COMPLETE = 2;	// non returning finally is optimized with only one instance of finally block
203 		final int FINALLY_MUST_BE_INLINED = 3;			// finally block must be inlined since cannot use jsr/ret bytecodes >1.5
204 		int finallyMode;
205 		if (subRoutineStartLabel == null) {
206 			finallyMode = NO_FINALLY;
207 		} else {
208 			if (this.isSubRoutineEscaping) {
209 				finallyMode = FINALLY_DOES_NOT_COMPLETE;
210 			} else if (scope.environment().options.inlineJsrBytecode) {
211 				finallyMode = FINALLY_MUST_BE_INLINED;
212 			} else {
213 				finallyMode = FINALLY_SUBROUTINE;
214 			}
215 		}
216 		boolean requiresNaturalExit = false;
217 		// preparing exception labels
218 		int maxCatches;
219 		ExceptionLabel[] exceptionLabels =
220 			new ExceptionLabel[maxCatches =
221 				catchArguments == null ? 0 : catchArguments.length];
222 		for (int i = 0; i < maxCatches; i++) {
223 			exceptionLabels[i] = new ExceptionLabel(codeStream, catchArguments[i].binding.type);
224 		}
225 		if (subRoutineStartLabel != null) {
226 			subRoutineStartLabel.initialize(codeStream);
227 			this.enterAnyExceptionHandler(codeStream);
228 		}
229 		// generate the try block
230 		tryBlock.generateCode(scope, codeStream);
231 		boolean tryBlockHasSomeCode = codeStream.position != pc;
232 		// flag telling if some bytecodes were issued inside the try block
233 
234 		// place end positions of user-defined exception labels
235 		if (tryBlockHasSomeCode) {
236 			// natural exit may require subroutine invocation (if finally != null)
237 			Label naturalExitLabel = new Label(codeStream);
238 			if (!tryBlockExit) {
239 				int position = codeStream.position;
240 				switch(finallyMode) {
241 					case FINALLY_SUBROUTINE :
242 					case FINALLY_MUST_BE_INLINED :
243 						requiresNaturalExit = true;
244 						// fall through
245 					case NO_FINALLY :
246 						codeStream.goto_(naturalExitLabel);
247 						break;
248 					case FINALLY_DOES_NOT_COMPLETE :
249 						codeStream.goto_(subRoutineStartLabel);
250 						break;
251 				}
252 				codeStream.updateLastRecordedEndPC(position);
253 				//goto is tagged as part of the try block
254 			}
255 			for (int i = 0; i < maxCatches; i++) {
256 				exceptionLabels[i].placeEnd();
257 			}
258 			/* generate sequence of handler, all starting by storing the TOS (exception
259 			thrown) into their own catch variables, the one specified in the source
260 			that must denote the handled exception.
261 			*/
262 			if (catchArguments != null) {
263 				for (int i = 0; i < maxCatches; i++) {
264 					// May loose some local variable initializations : affecting the local variable attributes
265 					if (preTryInitStateIndex != -1) {
266 						codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
267 					}
268 					exceptionLabels[i].place();
269 					codeStream.incrStackSize(1);
270 					// optimizing the case where the exception variable is not actually used
271 					LocalVariableBinding catchVar;
272 					int varPC = codeStream.position;
273 					if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
274 						codeStream.store(catchVar, false);
275 						catchVar.recordInitializationStartPC(codeStream.position);
276 						codeStream.addVisibleLocalVariable(catchVar);
277 					} else {
278 						codeStream.pop();
279 					}
280 					codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
281 					// Keep track of the pcs at diverging point for computing the local attribute
282 					// since not passing the catchScope, the block generation will exitUserScope(catchScope)
283 					catchBlocks[i].generateCode(scope, codeStream);
284 					if (!catchExits[i]) {
285 						switch(finallyMode) {
286 							case FINALLY_SUBROUTINE :
287 							case FINALLY_MUST_BE_INLINED :
288 								requiresNaturalExit = true;
289 								// fall through
290 							case NO_FINALLY :
291 								codeStream.goto_(naturalExitLabel);
292 								break;
293 							case FINALLY_DOES_NOT_COMPLETE :
294 								codeStream.goto_(subRoutineStartLabel);
295 								break;
296 						}
297 					}
298 				}
299 			}
300 			this.exitAnyExceptionHandler();
301 			// extra handler for trailing natural exit (will be fixed up later on when natural exit is generated below)
302 			ExceptionLabel naturalExitExceptionHandler =
303 				finallyMode == FINALLY_SUBROUTINE && requiresNaturalExit ? new ExceptionLabel(codeStream, null) : null;
304 
305 			// addition of a special handler so as to ensure that any uncaught exception (or exception thrown
306 			// inside catch blocks) will run the finally block
307 			int finallySequenceStartPC = codeStream.position;
308 			if (subRoutineStartLabel != null) {
309 				this.placeAllAnyExceptionHandlers();
310 				if (naturalExitExceptionHandler != null) naturalExitExceptionHandler.place();
311 
312 				if (preTryInitStateIndex != -1) {
313 					// reset initialization state, as for a normal catch block
314 					codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
315 				}
316 
317 				codeStream.incrStackSize(1);
318 				switch(finallyMode) {
319 					case FINALLY_SUBROUTINE :
320 						codeStream.store(anyExceptionVariable, false);
321 						codeStream.jsr(subRoutineStartLabel);
322 						codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
323 						int position = codeStream.position;
324 						codeStream.load(anyExceptionVariable);
325 						codeStream.athrow();
326 						codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
327 						subRoutineStartLabel.place();
328 						codeStream.incrStackSize(1);
329 						position = codeStream.position;
330 						codeStream.store(returnAddressVariable, false);
331 						codeStream.recordPositionsFrom(position, finallyBlock.sourceStart);
332 						finallyBlock.generateCode(scope, codeStream);
333 						position = codeStream.position;
334 						codeStream.ret(returnAddressVariable.resolvedPosition);
335 //						codeStream.updateLastRecordedEndPC(position);
336 						codeStream.recordPositionsFrom(
337 							position,
338 							finallyBlock.sourceEnd);
339 						// the ret bytecode is part of the subroutine
340 						break;
341 					case FINALLY_MUST_BE_INLINED :
342 						codeStream.store(anyExceptionVariable, false);
343 						codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
344 						this.finallyBlock.generateCode(currentScope, codeStream);
345 						position = codeStream.position;
346 						codeStream.load(anyExceptionVariable);
347 						codeStream.athrow();
348 						subRoutineStartLabel.place();
349 						codeStream.recordPositionsFrom(position, finallyBlock.sourceEnd);
350 						break;
351 					case FINALLY_DOES_NOT_COMPLETE :
352 						codeStream.pop();
353 						subRoutineStartLabel.place();
354 						codeStream.recordPositionsFrom(finallySequenceStartPC, finallyBlock.sourceStart);
355 						finallyBlock.generateCode(scope, codeStream);
356 						break;
357 				}
358 				// will naturally fall into subsequent code after subroutine invocation
359 				naturalExitLabel.place();
360 				if (requiresNaturalExit) {
361 					switch(finallyMode) {
362 						case FINALLY_SUBROUTINE :
363 							int position = codeStream.position;
364 							// fix up natural exit handler
365 							naturalExitExceptionHandler.placeStart();
366 							codeStream.jsr(subRoutineStartLabel);
367 							naturalExitExceptionHandler.placeEnd();
368 							codeStream.recordPositionsFrom(
369 								position,
370 								finallyBlock.sourceEnd);
371 							break;
372 						case FINALLY_MUST_BE_INLINED :
373 							// May loose some local variable initializations : affecting the local variable attributes
374 							// needed since any exception handler got inlined subroutine
375 							if (preTryInitStateIndex != -1) {
376 								codeStream.removeNotDefinitelyAssignedVariables(currentScope, preTryInitStateIndex);
377 							}
378 							// entire sequence for finally is associated to finally block
379 							finallyBlock.generateCode(scope, codeStream);
380 							break;
381 						case FINALLY_DOES_NOT_COMPLETE :
382 							break;
383 					}
384 				}
385 			} else {
386 				// no subroutine, simply position end label (natural exit == end)
387 				naturalExitLabel.place();
388 			}
389 		} else {
390 			// try block had no effect, only generate the body of the finally block if any
391 			if (subRoutineStartLabel != null) {
392 				finallyBlock.generateCode(scope, codeStream);
393 			}
394 		}
395 		// May loose some local variable initializations : affecting the local variable attributes
396 		if (mergedInitStateIndex != -1) {
397 			codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
398 			codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
399 		}
400 		codeStream.recordPositionsFrom(pc, this.sourceStart);
401 	}
402 
403 	/* (non-Javadoc)
404 	 * @see org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement#generateSubRoutineInvocation(org.eclipse.jdt.internal.compiler.lookup.BlockScope, org.eclipse.jdt.internal.compiler.codegen.CodeStream)
405 	 */
generateSubRoutineInvocation( BlockScope currentScope, CodeStream codeStream)406 	public void generateSubRoutineInvocation(
407 			BlockScope currentScope,
408 			CodeStream codeStream) {
409 
410 		if (this.isSubRoutineEscaping) {
411 				codeStream.goto_(this.subRoutineStartLabel);
412 		} else {
413 			if (currentScope.environment().options.inlineJsrBytecode) {
414 				// cannot use jsr bytecode, then simply inline the subroutine
415 				this.finallyBlock.generateCode(currentScope, codeStream);
416 			} else {
417 				// classic subroutine invocation, distinguish case of non-returning subroutine
418 				codeStream.jsr(this.subRoutineStartLabel);
419 			}
420 		}
421 	}
422 
printStatement(int indent, StringBuffer output)423 	public StringBuffer printStatement(int indent, StringBuffer output) {
424 		printIndent(indent, output).append("try \n"); //$NON-NLS-1$
425 		tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
426 
427 		//catches
428 		if (catchBlocks != null)
429 			for (int i = 0; i < catchBlocks.length; i++) {
430 					output.append('\n');
431 					printIndent(indent, output).append("catch ("); //$NON-NLS-1$
432 					catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
433 					catchBlocks[i].printStatement(indent + 1, output);
434 			}
435 		//finally
436 		if (finallyBlock != null) {
437 			output.append('\n');
438 			printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
439 			finallyBlock.printStatement(indent + 1, output);
440 		}
441 
442 		return output;
443 	}
444 
resolve(BlockScope upperScope)445 	public void resolve(BlockScope upperScope) {
446 
447 		// special scope for secret locals optimization.
448 		this.scope = new BlockScope(upperScope);
449 
450 		BlockScope tryScope = new BlockScope(scope);
451 		BlockScope finallyScope = null;
452 
453 		if (finallyBlock != null) {
454 			if (finallyBlock.isEmptyBlock()) {
455 				if ((finallyBlock.bits & UndocumentedEmptyBlockMASK) != 0) {
456 					scope.problemReporter().undocumentedEmptyBlock(finallyBlock.sourceStart, finallyBlock.sourceEnd);
457 				}
458 			} else {
459 				finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
460 
461 				// provision for returning and forcing the finally block to run
462 				MethodScope methodScope = scope.methodScope();
463 
464 				// the type does not matter as long as it is not a base type
465 				if (!upperScope.environment().options.inlineJsrBytecode) {
466 					this.returnAddressVariable =
467 						new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
468 					finallyScope.addLocalVariable(returnAddressVariable);
469 					this.returnAddressVariable.setConstant(NotAConstant); // not inlinable
470 				}
471 				this.subRoutineStartLabel = new Label();
472 
473 				this.anyExceptionVariable =
474 					new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
475 				finallyScope.addLocalVariable(this.anyExceptionVariable);
476 				this.anyExceptionVariable.setConstant(NotAConstant); // not inlinable
477 
478 				if (!methodScope.isInsideInitializer()) {
479 					MethodBinding methodBinding =
480 						((AbstractMethodDeclaration) methodScope.referenceContext).binding;
481 					if (methodBinding != null) {
482 						TypeBinding methodReturnType = methodBinding.returnType;
483 						if (methodReturnType.id != T_void) {
484 							this.secretReturnValue =
485 								new LocalVariableBinding(
486 									SecretLocalDeclarationName,
487 									methodReturnType,
488 									AccDefault,
489 									false);
490 							finallyScope.addLocalVariable(this.secretReturnValue);
491 							this.secretReturnValue.setConstant(NotAConstant); // not inlinable
492 						}
493 					}
494 				}
495 				finallyBlock.resolveUsing(finallyScope);
496 				// force the finally scope to have variable positions shifted after its try scope and catch ones
497 				finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
498 				finallyScope.shiftScopes[0] = tryScope;
499 			}
500 		}
501 		this.tryBlock.resolveUsing(tryScope);
502 
503 		// arguments type are checked against JavaLangThrowable in resolveForCatch(..)
504 		if (this.catchBlocks != null) {
505 			int length = this.catchArguments.length;
506 			TypeBinding[] argumentTypes = new TypeBinding[length];
507 			boolean catchHasError = false;
508 			for (int i = 0; i < length; i++) {
509 				BlockScope catchScope = new BlockScope(scope);
510 				if (finallyScope != null){
511 					finallyScope.shiftScopes[i+1] = catchScope;
512 				}
513 				// side effect on catchScope in resolveForCatch(..)
514 				if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null) {
515 					catchHasError = true;
516 				}
517 				catchBlocks[i].resolveUsing(catchScope);
518 			}
519 			if (catchHasError) {
520 				return;
521 			}
522 			// Verify that the catch clause are ordered in the right way:
523 			// more specialized first.
524 			this.caughtExceptionTypes = new ReferenceBinding[length];
525 			for (int i = 0; i < length; i++) {
526 				caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
527 				for (int j = 0; j < i; j++) {
528 					if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
529 						scope.problemReporter().wrongSequenceOfExceptionTypesError(this, caughtExceptionTypes[i], i, argumentTypes[j]);
530 					}
531 				}
532 			}
533 		} else {
534 			caughtExceptionTypes = new ReferenceBinding[0];
535 		}
536 
537 		if (finallyScope != null){
538 			// add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
539 			// the shifting is necessary to achieve no overlay in between the finally scope and its
540 			// sibling in term of local variable positions.
541 			this.scope.addSubscope(finallyScope);
542 		}
543 	}
544 
traverse( ASTVisitor visitor, BlockScope blockScope)545 	public void traverse(
546 		ASTVisitor visitor,
547 		BlockScope blockScope) {
548 
549 		if (visitor.visit(this, blockScope)) {
550 			tryBlock.traverse(visitor, scope);
551 			if (catchArguments != null) {
552 				for (int i = 0, max = catchBlocks.length; i < max; i++) {
553 					catchArguments[i].traverse(visitor, scope);
554 					catchBlocks[i].traverse(visitor, scope);
555 				}
556 			}
557 			if (finallyBlock != null)
558 				finallyBlock.traverse(visitor, scope);
559 		}
560 		visitor.endVisit(this, blockScope);
561 	}
562 }
563