1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.*;
15 import net.sourceforge.phpdt.internal.compiler.flow.*;
16 import net.sourceforge.phpdt.internal.compiler.lookup.*;
18 public class TryStatement extends Statement {
20 public Block tryBlock;
21 public Block[] catchBlocks;
22 public Argument[] catchArguments;
23 public Block finallyBlock;
26 public boolean subRoutineCannotReturn = true;
27 // should rename into subRoutineComplete to be set to false by default
29 ReferenceBinding[] caughtExceptionTypes;
32 public int[] preserveExceptionHandler;
34 Label subRoutineStartLabel;
35 public LocalVariableBinding anyExceptionVariable,
36 returnAddressVariable,
39 public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
40 public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
41 public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
43 // for local variables table attributes
44 int preTryInitStateIndex = -1;
45 int mergedInitStateIndex = -1;
47 public FlowInfo analyseCode(
48 BlockScope currentScope,
49 FlowContext flowContext,
52 // Consider the try block and catch block so as to compute the intersection of initializations and
53 // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
54 // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
55 // complete, then only keep this result for the rest of the analysis
57 // process the finally block (subroutine) - create a context for the subroutine
59 preTryInitStateIndex =
60 currentScope.methodScope().recordInitializationStates(flowInfo);
62 if (anyExceptionVariable != null) {
63 anyExceptionVariable.used = true;
65 if (returnAddressVariable != null) {
66 returnAddressVariable.used = true;
68 InsideSubRoutineFlowContext insideSubContext;
69 FinallyFlowContext finallyContext;
70 UnconditionalFlowInfo subInfo;
71 if (subRoutineStartLabel == null) {
73 insideSubContext = null;
74 finallyContext = null;
77 // analyse finally block first
78 insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
83 finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
85 .unconditionalInits();
86 if (!((subInfo == FlowInfo.DeadEnd) || subInfo.isFakeReachable())) {
87 subRoutineCannotReturn = false;
90 // process the try block in a context handling the local exceptions.
91 ExceptionHandlingFlowContext handlingContext =
92 new ExceptionHandlingFlowContext(
93 insideSubContext == null ? flowContext : insideSubContext,
97 flowInfo.unconditionalInits());
100 if (tryBlock.statements == null) {
102 tryBlockExit = false;
104 tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
105 tryBlockExit = (tryInfo == FlowInfo.DeadEnd) || tryInfo.isFakeReachable();
108 // check unreachable catch blocks
109 handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
111 // process the catch blocks - computing the minimal exit depth amongst try/catch
112 if (catchArguments != null) {
114 catchExits = new boolean[catchCount = catchBlocks.length];
115 for (int i = 0; i < catchCount; i++) {
116 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
121 .unconditionalInits()
122 .addPotentialInitializationsFrom(
123 handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
124 .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
125 .addPotentialInitializationsFrom(handlingContext.initsOnReturn);
127 // catch var is always set
128 catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
130 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
131 the try block that need to be detected , e.g.
132 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
133 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
134 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
136 if (tryBlock.statements == null) {
137 catchInfo.markAsFakeReachable(true);
140 catchBlocks[i].analyseCode(
142 insideSubContext == null ? flowContext : insideSubContext,
145 ((catchInfo == FlowInfo.DeadEnd) || catchInfo.isFakeReachable());
146 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
149 if (subRoutineStartLabel == null) {
150 mergedInitStateIndex =
151 currentScope.methodScope().recordInitializationStates(tryInfo);
155 // we also need to check potential multiple assignments of final variables inside the finally block
156 // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
157 tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn);
158 finallyContext.complainOnRedundantFinalAssignments(tryInfo, currentScope);
159 if (subInfo == FlowInfo.DeadEnd) {
160 mergedInitStateIndex =
161 currentScope.methodScope().recordInitializationStates(subInfo);
164 FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
165 mergedInitStateIndex =
166 currentScope.methodScope().recordInitializationStates(mergedInfo);
171 public boolean cannotReturn() {
173 return subRoutineCannotReturn;
177 * Try statement code generation
180 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
182 if ((bits & IsReachableMASK) == 0) {
185 if (tryBlock.isEmptyBlock()) {
186 if (subRoutineStartLabel != null) {
187 // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
188 finallyBlock.generateCode(scope, codeStream);
190 // May loose some local variable initializations : affecting the local variable attributes
191 if (mergedInitStateIndex != -1) {
192 codeStream.removeNotDefinitelyAssignedVariables(
194 mergedInitStateIndex);
196 // no local bytecode produced so no need for position remembering
199 int pc = codeStream.position;
200 Label endLabel = new Label(codeStream);
201 boolean requiresNaturalJsr = false;
203 // preparing exception labels
205 ExceptionLabel[] exceptionLabels =
206 new ExceptionLabel[maxCatches =
207 catchArguments == null ? 0 : catchArguments.length];
208 for (int i = 0; i < maxCatches; i++) {
209 boolean preserveCurrentHandler =
210 (preserveExceptionHandler[i
211 / ExceptionHandlingFlowContext.BitCacheSize]
212 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
214 if (preserveCurrentHandler) {
218 (ReferenceBinding) catchArguments[i].binding.type);
221 ExceptionLabel anyExceptionLabel = null;
222 if (subRoutineStartLabel != null) {
223 subRoutineStartLabel.codeStream = codeStream;
224 anyExceptionLabel = new ExceptionLabel(codeStream, null);
226 // generate the try block
227 tryBlock.generateCode(scope, codeStream);
228 boolean tryBlockHasSomeCode = codeStream.position != pc;
229 // flag telling if some bytecodes were issued inside the try block
231 // natural exit: only if necessary
232 boolean nonReturningSubRoutine =
233 (subRoutineStartLabel != null) && subRoutineCannotReturn;
234 if ((!tryBlockExit) && tryBlockHasSomeCode) {
235 int position = codeStream.position;
236 if (nonReturningSubRoutine) {
237 codeStream.goto_(subRoutineStartLabel);
239 requiresNaturalJsr = true;
240 codeStream.goto_(endLabel);
242 codeStream.updateLastRecordedEndPC(position);
243 //goto is tagged as part of the try block
245 // place end positions of user-defined exception labels
246 if (tryBlockHasSomeCode) {
247 for (int i = 0; i < maxCatches; i++) {
248 boolean preserveCurrentHandler =
249 (preserveExceptionHandler[i
250 / ExceptionHandlingFlowContext.BitCacheSize]
251 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
253 if (preserveCurrentHandler) {
254 exceptionLabels[i].placeEnd();
257 /* generate sequence of handler, all starting by storing the TOS (exception
258 thrown) into their own catch variables, the one specified in the source
259 that must denote the handled exception.
261 if (catchArguments == null) {
262 if (anyExceptionLabel != null) {
263 anyExceptionLabel.placeEnd();
266 for (int i = 0; i < maxCatches; i++) {
267 boolean preserveCurrentHandler =
268 (preserveExceptionHandler[i
269 / ExceptionHandlingFlowContext.BitCacheSize]
270 & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
272 if (preserveCurrentHandler) {
273 // May loose some local variable initializations : affecting the local variable attributes
274 if (preTryInitStateIndex != -1) {
275 codeStream.removeNotDefinitelyAssignedVariables(
277 preTryInitStateIndex);
279 exceptionLabels[i].place();
280 codeStream.incrStackSize(1);
281 // optimizing the case where the exception variable is not actually used
282 LocalVariableBinding catchVar;
283 int varPC = codeStream.position;
284 if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
285 codeStream.store(catchVar, false);
286 catchVar.recordInitializationStartPC(codeStream.position);
287 codeStream.addVisibleLocalVariable(catchVar);
291 codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
292 // Keep track of the pcs at diverging point for computing the local attribute
293 // since not passing the catchScope, the block generation will exitUserScope(catchScope)
294 catchBlocks[i].generateCode(scope, codeStream);
296 if (i == maxCatches - 1) {
297 if (anyExceptionLabel != null) {
298 anyExceptionLabel.placeEnd();
300 if (subRoutineStartLabel != null) {
301 if (!catchExits[i] && preserveCurrentHandler) {
302 requiresNaturalJsr = true;
303 codeStream.goto_(endLabel);
307 if (!catchExits[i] && preserveCurrentHandler) {
308 if (nonReturningSubRoutine) {
309 codeStream.goto_(subRoutineStartLabel);
311 requiresNaturalJsr = true;
312 codeStream.goto_(endLabel);
318 // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
319 // inside catch blocks) will run the finally block
320 int finallySequenceStartPC = codeStream.position;
321 if (subRoutineStartLabel != null) {
322 // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
323 anyExceptionLabel.place();
325 if (preTryInitStateIndex != -1) {
326 // reset initialization state, as for a normal catch block
327 codeStream.removeNotDefinitelyAssignedVariables(
329 preTryInitStateIndex);
332 codeStream.incrStackSize(1);
333 if (nonReturningSubRoutine) {
335 // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
337 codeStream.store(anyExceptionVariable, false);
338 codeStream.jsr(subRoutineStartLabel);
339 codeStream.load(anyExceptionVariable);
343 // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
345 if (subRoutineStartLabel != null) {
346 if (nonReturningSubRoutine) {
347 requiresNaturalJsr = false;
349 Label veryEndLabel = new Label(codeStream);
350 if (requiresNaturalJsr) {
351 codeStream.jsr(subRoutineStartLabel);
352 codeStream.goto_(veryEndLabel);
354 subRoutineStartLabel.place();
355 if (!nonReturningSubRoutine) {
356 codeStream.incrStackSize(1);
357 codeStream.store(returnAddressVariable, false);
359 codeStream.recordPositionsFrom(
360 finallySequenceStartPC,
361 finallyBlock.sourceStart);
362 // entire sequence for finally is associated to finally block
363 finallyBlock.generateCode(scope, codeStream);
364 if (!nonReturningSubRoutine) {
365 int position = codeStream.position;
366 codeStream.ret(returnAddressVariable.resolvedPosition);
367 codeStream.updateLastRecordedEndPC(position);
368 // the ret bytecode is part of the subroutine
370 if (requiresNaturalJsr) {
371 veryEndLabel.place();
375 // try block had no effect, only generate the body of the finally block if any
376 if (subRoutineStartLabel != null) {
377 finallyBlock.generateCode(scope, codeStream);
380 // May loose some local variable initializations : affecting the local variable attributes
381 if (mergedInitStateIndex != -1) {
382 codeStream.removeNotDefinitelyAssignedVariables(
384 mergedInitStateIndex);
385 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
387 codeStream.recordPositionsFrom(pc, this.sourceStart);
390 public void resolve(BlockScope upperScope) {
392 // special scope for secret locals optimization.
393 this.scope = new BlockScope(upperScope);
395 BlockScope tryScope = new BlockScope(scope);
396 BlockScope finallyScope = null;
398 if (finallyBlock != null
399 && finallyBlock.statements != null) {
401 finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
403 // provision for returning and forcing the finally block to run
404 MethodScope methodScope = scope.methodScope();
406 // the type does not matter as long as its not a normal base type
407 this.returnAddressVariable =
408 new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
409 finallyScope.addLocalVariable(returnAddressVariable);
410 this.returnAddressVariable.constant = NotAConstant; // not inlinable
411 this.subRoutineStartLabel = new Label();
413 this.anyExceptionVariable =
414 new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
415 finallyScope.addLocalVariable(this.anyExceptionVariable);
416 this.anyExceptionVariable.constant = NotAConstant; // not inlinable
418 if (!methodScope.isInsideInitializer()) {
419 MethodBinding methodBinding =
420 ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
421 if (methodBinding != null) {
422 TypeBinding methodReturnType = methodBinding.returnType;
423 if (methodReturnType.id != T_void) {
424 this.secretReturnValue =
425 new LocalVariableBinding(
426 SecretLocalDeclarationName,
430 finallyScope.addLocalVariable(this.secretReturnValue);
431 this.secretReturnValue.constant = NotAConstant; // not inlinable
435 finallyBlock.resolveUsing(finallyScope);
436 // force the finally scope to have variable positions shifted after its try scope and catch ones
437 finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
438 finallyScope.shiftScopes[0] = tryScope;
440 this.tryBlock.resolveUsing(tryScope);
442 // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
443 if (this.catchBlocks != null) {
444 int length = this.catchArguments.length;
445 TypeBinding[] argumentTypes = new TypeBinding[length];
446 for (int i = 0; i < length; i++) {
447 BlockScope catchScope = new BlockScope(scope);
448 if (finallyScope != null){
449 finallyScope.shiftScopes[i+1] = catchScope;
451 // side effect on catchScope in resolveForCatch(..)
452 if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null)
454 catchBlocks[i].resolveUsing(catchScope);
457 // Verify that the catch clause are ordered in the right way:
458 // more specialized first.
459 this.caughtExceptionTypes = new ReferenceBinding[length];
460 for (int i = 0; i < length; i++) {
461 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
462 for (int j = 0; j < i; j++) {
463 if (scope.areTypesCompatible(caughtExceptionTypes[i], argumentTypes[j])) {
464 scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
470 caughtExceptionTypes = new ReferenceBinding[0];
473 if (finallyScope != null){
474 // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
475 // the shifting is necessary to achieve no overlay in between the finally scope and its
476 // sibling in term of local variable positions.
477 this.scope.addSubscope(finallyScope);
481 public String toString(int tab) {
482 String s = tabString(tab);
484 s = s + "try "; //$NON-NLS-1$
485 if (tryBlock == Block.None)
486 s = s + "{}"; //$NON-NLS-1$
488 s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
491 if (catchBlocks != null)
492 for (int i = 0; i < catchBlocks.length; i++)
493 s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
494 +catchArguments[i].toString(0) + ") " //$NON-NLS-1$
495 +catchBlocks[i].toString(tab + 1);
497 if (finallyBlock != null) {
498 if (finallyBlock == Block.None)
499 s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
501 s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
502 finallyBlock.toString(tab + 1);
508 public void traverse(
509 IAbstractSyntaxTreeVisitor visitor,
510 BlockScope blockScope) {
512 if (visitor.visit(this, blockScope)) {
513 tryBlock.traverse(visitor, scope);
514 if (catchArguments != null) {
515 for (int i = 0, max = catchBlocks.length; i < max; i++) {
516 catchArguments[i].traverse(visitor, scope);
517 catchBlocks[i].traverse(visitor, scope);
520 if (finallyBlock != null)
521 finallyBlock.traverse(visitor, scope);
523 visitor.endVisit(this, blockScope);