1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
15 import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FinallyFlowContext;
17 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
18 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
19 import net.sourceforge.phpdt.internal.compiler.flow.InsideSubRoutineFlowContext;
20 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
21 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
24 import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
25 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
26 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
28 public class TryStatement extends Statement {
30 public Block tryBlock;
31 public Block[] catchBlocks;
32 public Argument[] catchArguments;
33 public Block finallyBlock;
36 public boolean subRoutineCannotReturn = true;
37 public UnconditionalFlowInfo subRoutineInits;
39 // should rename into subRoutineComplete to be set to false by default
41 ReferenceBinding[] caughtExceptionTypes;
44 public int[] preserveExceptionHandler;
46 Label subRoutineStartLabel;
47 public LocalVariableBinding anyExceptionVariable,
48 returnAddressVariable,
51 public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
52 public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
53 public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
55 // for local variables table attributes
56 int preTryInitStateIndex = -1;
57 int mergedInitStateIndex = -1;
59 public FlowInfo analyseCode(
60 BlockScope currentScope,
61 FlowContext flowContext,
64 // Consider the try block and catch block so as to compute the intersection of initializations and
65 // the minimum exit relative depth amongst all of them. Then consider the subroutine, and append its
66 // initialization to the try/catch ones, if the subroutine completes normally. If the subroutine does not
67 // complete, then only keep this result for the rest of the analysis
69 // process the finally block (subroutine) - create a context for the subroutine
71 preTryInitStateIndex =
72 currentScope.methodScope().recordInitializationStates(flowInfo);
74 if (anyExceptionVariable != null) {
75 anyExceptionVariable.useFlag = LocalVariableBinding.USED;
77 if (returnAddressVariable != null) {
78 returnAddressVariable.useFlag = LocalVariableBinding.USED;
80 InsideSubRoutineFlowContext insideSubContext;
81 FinallyFlowContext finallyContext;
82 UnconditionalFlowInfo subInfo;
83 if (subRoutineStartLabel == null) {
85 insideSubContext = null;
86 finallyContext = null;
89 // analyse finally block first
90 insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
95 finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
97 .unconditionalInits();
98 if (subInfo.isReachable()) {
99 subRoutineCannotReturn = false;
101 this.subRoutineInits = subInfo;
103 // process the try block in a context handling the local exceptions.
104 ExceptionHandlingFlowContext handlingContext =
105 new ExceptionHandlingFlowContext(
106 insideSubContext == null ? flowContext : insideSubContext,
108 caughtExceptionTypes,
110 flowInfo.unconditionalInits());
113 if (tryBlock.statements == null) {
115 tryBlockExit = false;
117 tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
118 tryBlockExit = !tryInfo.isReachable();
121 // check unreachable catch blocks
122 // handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
124 // process the catch blocks - computing the minimal exit depth amongst try/catch
125 if (catchArguments != null) {
127 catchExits = new boolean[catchCount = catchBlocks.length];
128 for (int i = 0; i < catchCount; i++) {
129 // keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
133 .unconditionalInits()
134 .addPotentialInitializationsFrom(
135 handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
136 .addPotentialInitializationsFrom(tryInfo.unconditionalInits())
137 .addPotentialInitializationsFrom(handlingContext.initsOnReturn);
139 // catch var is always set
140 catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
142 "If we are about to consider an unchecked exception handler, potential inits may have occured inside
143 the try block that need to be detected , e.g.
144 try { x = 1; throwSomething();} catch(Exception e){ x = 2} "
145 "(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
146 ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
148 // TODO: should only tag as unreachable if the catchblock cannot be reached
149 //??? if (!handlingContext.initsOnException(caughtExceptionTypes[i]).isReachable()){
150 if (tryBlock.statements == null) {
151 catchInfo.setReachMode(FlowInfo.UNREACHABLE);
154 catchBlocks[i].analyseCode(
156 insideSubContext == null ? flowContext : insideSubContext,
158 catchExits[i] = !catchInfo.isReachable();
159 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
162 if (subRoutineStartLabel == null) {
163 mergedInitStateIndex =
164 currentScope.methodScope().recordInitializationStates(tryInfo);
169 // we also need to check potential multiple assignments of final variables inside the finally block
170 // need to include potential inits from returns inside the try/catch parts - 1GK2AOF
171 finallyContext.complainOnRedundantFinalAssignments(
172 tryInfo.isReachable()
173 ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
174 : insideSubContext.initsOnReturn,
176 if (subInfo == FlowInfo.DEAD_END) {
177 mergedInitStateIndex =
178 currentScope.methodScope().recordInitializationStates(subInfo);
181 FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
182 mergedInitStateIndex =
183 currentScope.methodScope().recordInitializationStates(mergedInfo);
188 public boolean cannotReturn() {
190 return subRoutineCannotReturn;
194 * Try statement code generation
197 // public void generateCode(BlockScope currentScope, CodeStream codeStream) {
199 // if ((bits & IsReachableMASK) == 0) {
202 // if (tryBlock.isEmptyBlock()) {
203 // if (subRoutineStartLabel != null) {
204 // // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
205 // finallyBlock.generateCode(scope, codeStream);
207 // // May loose some local variable initializations : affecting the local variable attributes
208 // if (mergedInitStateIndex != -1) {
209 // codeStream.removeNotDefinitelyAssignedVariables(
211 // mergedInitStateIndex);
213 // // no local bytecode produced so no need for position remembering
216 // int pc = codeStream.position;
217 // Label endLabel = new Label(codeStream);
218 // boolean requiresNaturalJsr = false;
220 // // preparing exception labels
222 // ExceptionLabel[] exceptionLabels =
223 // new ExceptionLabel[maxCatches =
224 // catchArguments == null ? 0 : catchArguments.length];
225 // for (int i = 0; i < maxCatches; i++) {
226 // boolean preserveCurrentHandler =
227 // (preserveExceptionHandler[i
228 // / ExceptionHandlingFlowContext.BitCacheSize]
229 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
231 // if (preserveCurrentHandler) {
232 // exceptionLabels[i] =
233 // new ExceptionLabel(
235 // (ReferenceBinding) catchArguments[i].binding.type);
238 // ExceptionLabel anyExceptionLabel = null;
239 // if (subRoutineStartLabel != null) {
240 // subRoutineStartLabel.codeStream = codeStream;
241 // anyExceptionLabel = new ExceptionLabel(codeStream, null);
243 // // generate the try block
244 // tryBlock.generateCode(scope, codeStream);
245 // boolean tryBlockHasSomeCode = codeStream.position != pc;
246 // // flag telling if some bytecodes were issued inside the try block
248 // // natural exit: only if necessary
249 // boolean nonReturningSubRoutine =
250 // (subRoutineStartLabel != null) && subRoutineCannotReturn;
251 // if ((!tryBlockExit) && tryBlockHasSomeCode) {
252 // int position = codeStream.position;
253 // if (nonReturningSubRoutine) {
254 // codeStream.goto_(subRoutineStartLabel);
256 // requiresNaturalJsr = true;
257 // codeStream.goto_(endLabel);
259 // codeStream.updateLastRecordedEndPC(position);
260 // //goto is tagged as part of the try block
262 // // place end positions of user-defined exception labels
263 // if (tryBlockHasSomeCode) {
264 // for (int i = 0; i < maxCatches; i++) {
265 // boolean preserveCurrentHandler =
266 // (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
267 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
269 // if (preserveCurrentHandler) {
270 // exceptionLabels[i].placeEnd();
273 // /* generate sequence of handler, all starting by storing the TOS (exception
274 // thrown) into their own catch variables, the one specified in the source
275 // that must denote the handled exception.
277 // if (catchArguments == null) {
278 // if (anyExceptionLabel != null) {
279 // anyExceptionLabel.placeEnd();
282 // for (int i = 0; i < maxCatches; i++) {
283 // boolean preserveCurrentHandler =
284 // (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
285 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
287 // if (preserveCurrentHandler) {
288 // // May loose some local variable initializations : affecting the local variable attributes
289 // if (preTryInitStateIndex != -1) {
290 // codeStream.removeNotDefinitelyAssignedVariables(
292 // preTryInitStateIndex);
294 // exceptionLabels[i].place();
295 // codeStream.incrStackSize(1);
296 // // optimizing the case where the exception variable is not actually used
297 // LocalVariableBinding catchVar;
298 // int varPC = codeStream.position;
299 // if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
300 // codeStream.store(catchVar, false);
301 // catchVar.recordInitializationStartPC(codeStream.position);
302 // codeStream.addVisibleLocalVariable(catchVar);
306 // codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
307 // // Keep track of the pcs at diverging point for computing the local attribute
308 // // since not passing the catchScope, the block generation will exitUserScope(catchScope)
309 // catchBlocks[i].generateCode(scope, codeStream);
311 // if (i == maxCatches - 1) {
312 // if (anyExceptionLabel != null) {
313 // anyExceptionLabel.placeEnd();
315 // if (subRoutineStartLabel != null) {
316 // if (!catchExits[i] && preserveCurrentHandler) {
317 // requiresNaturalJsr = true;
318 // codeStream.goto_(endLabel);
322 // if (!catchExits[i] && preserveCurrentHandler) {
323 // if (nonReturningSubRoutine) {
324 // codeStream.goto_(subRoutineStartLabel);
326 // requiresNaturalJsr = true;
327 // codeStream.goto_(endLabel);
333 // // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
334 // // inside catch blocks) will run the finally block
335 // int finallySequenceStartPC = codeStream.position;
336 // if (subRoutineStartLabel != null) {
337 // // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
338 // anyExceptionLabel.place();
340 // if (preTryInitStateIndex != -1) {
341 // // reset initialization state, as for a normal catch block
342 // codeStream.removeNotDefinitelyAssignedVariables(
344 // preTryInitStateIndex);
347 // codeStream.incrStackSize(1);
348 // if (nonReturningSubRoutine) {
350 // // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
352 // codeStream.store(anyExceptionVariable, false);
353 // codeStream.jsr(subRoutineStartLabel);
354 // codeStream.load(anyExceptionVariable);
355 // codeStream.athrow();
358 // // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
360 // if (subRoutineStartLabel != null) {
361 // if (nonReturningSubRoutine) {
362 // requiresNaturalJsr = false;
364 // Label veryEndLabel = new Label(codeStream);
365 // if (requiresNaturalJsr) {
366 // codeStream.jsr(subRoutineStartLabel);
367 // codeStream.goto_(veryEndLabel);
369 // subRoutineStartLabel.place();
370 // if (!nonReturningSubRoutine) {
371 // codeStream.incrStackSize(1);
372 // codeStream.store(returnAddressVariable, false);
374 // codeStream.recordPositionsFrom(
375 // finallySequenceStartPC,
376 // finallyBlock.sourceStart);
377 // // entire sequence for finally is associated to finally block
378 // finallyBlock.generateCode(scope, codeStream);
379 // if (!nonReturningSubRoutine) {
380 // int position = codeStream.position;
381 // codeStream.ret(returnAddressVariable.resolvedPosition);
382 // codeStream.updateLastRecordedEndPC(position);
383 // // the ret bytecode is part of the subroutine
385 // if (requiresNaturalJsr) {
386 // veryEndLabel.place();
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);
395 // // May loose some local variable initializations : affecting the local variable attributes
396 // if (mergedInitStateIndex != -1) {
397 // codeStream.removeNotDefinitelyAssignedVariables(
399 // mergedInitStateIndex);
400 // codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
402 // codeStream.recordPositionsFrom(pc, this.sourceStart);
405 public void resetStateForCodeGeneration() {
406 if (this.subRoutineStartLabel != null) {
407 this.subRoutineStartLabel.resetStateForCodeGeneration();
410 public StringBuffer printStatement(int indent, StringBuffer output) {
411 printIndent(indent, output).append("try \n"); //$NON-NLS-1$
412 tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
415 if (catchBlocks != null)
416 for (int i = 0; i < catchBlocks.length; i++) {
418 printIndent(indent, output).append("catch ("); //$NON-NLS-1$
419 catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
420 catchBlocks[i].printStatement(indent + 1, output);
423 if (finallyBlock != null) {
425 printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
426 finallyBlock.printStatement(indent + 1, output);
431 public void resolve(BlockScope upperScope) {
433 // special scope for secret locals optimization.
434 this.scope = new BlockScope(upperScope);
436 BlockScope tryScope = new BlockScope(scope);
437 BlockScope finallyScope = null;
439 if (finallyBlock != null
440 && finallyBlock.statements != null) {
442 finallyScope = new BlockScope(scope, false); // don't add it yet to parent scope
444 // provision for returning and forcing the finally block to run
445 MethodScope methodScope = scope.methodScope();
447 // the type does not matter as long as it is not a base type
448 this.returnAddressVariable =
449 new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
450 finallyScope.addLocalVariable(returnAddressVariable);
451 this.returnAddressVariable.constant = NotAConstant; // not inlinable
452 this.subRoutineStartLabel = new Label();
454 this.anyExceptionVariable =
455 new LocalVariableBinding(SecretAnyHandlerName, scope.getJavaLangThrowable(), AccDefault, false);
456 finallyScope.addLocalVariable(this.anyExceptionVariable);
457 this.anyExceptionVariable.constant = NotAConstant; // not inlinable
459 if (!methodScope.isInsideInitializer()) {
460 MethodBinding methodBinding =
461 ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
462 if (methodBinding != null) {
463 TypeBinding methodReturnType = methodBinding.returnType;
464 if (methodReturnType.id != T_void) {
465 this.secretReturnValue =
466 new LocalVariableBinding(
467 SecretLocalDeclarationName,
471 finallyScope.addLocalVariable(this.secretReturnValue);
472 this.secretReturnValue.constant = NotAConstant; // not inlinable
476 finallyBlock.resolveUsing(finallyScope);
477 // force the finally scope to have variable positions shifted after its try scope and catch ones
478 finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1 : catchArguments.length+1];
479 finallyScope.shiftScopes[0] = tryScope;
481 this.tryBlock.resolveUsing(tryScope);
483 // arguments type are checked against JavaLangThrowable in resolveForCatch(..)
484 if (this.catchBlocks != null) {
485 int length = this.catchArguments.length;
486 TypeBinding[] argumentTypes = new TypeBinding[length];
487 for (int i = 0; i < length; i++) {
488 BlockScope catchScope = new BlockScope(scope);
489 if (finallyScope != null){
490 finallyScope.shiftScopes[i+1] = catchScope;
492 // side effect on catchScope in resolveForCatch(..)
493 if ((argumentTypes[i] = catchArguments[i].resolveForCatch(catchScope)) == null)
495 catchBlocks[i].resolveUsing(catchScope);
498 // Verify that the catch clause are ordered in the right way:
499 // more specialized first.
500 this.caughtExceptionTypes = new ReferenceBinding[length];
501 for (int i = 0; i < length; i++) {
502 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
503 for (int j = 0; j < i; j++) {
504 if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
505 scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
506 // cannot return - since may still proceed if unreachable code is ignored (21203)
511 caughtExceptionTypes = new ReferenceBinding[0];
514 if (finallyScope != null){
515 // add finallyScope as last subscope, so it can be shifted behind try/catch subscopes.
516 // the shifting is necessary to achieve no overlay in between the finally scope and its
517 // sibling in term of local variable positions.
518 this.scope.addSubscope(finallyScope);
522 public String toString(int tab) {
523 String s = tabString(tab);
525 s = s + "try "; //$NON-NLS-1$
526 if (tryBlock == Block.None)
527 s = s + "{}"; //$NON-NLS-1$
529 s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
532 if (catchBlocks != null)
533 for (int i = 0; i < catchBlocks.length; i++)
534 s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
535 +catchArguments[i].toString(0) + ") " //$NON-NLS-1$
536 +catchBlocks[i].toString(tab + 1);
538 if (finallyBlock != null) {
539 if (finallyBlock == Block.None)
540 s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
542 s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
543 finallyBlock.toString(tab + 1);
549 public void traverse(
551 BlockScope blockScope) {
553 if (visitor.visit(this, blockScope)) {
554 tryBlock.traverse(visitor, scope);
555 if (catchArguments != null) {
556 for (int i = 0, max = catchBlocks.length; i < max; i++) {
557 catchArguments[i].traverse(visitor, scope);
558 catchBlocks[i].traverse(visitor, scope);
561 if (finallyBlock != null)
562 finallyBlock.traverse(visitor, scope);
564 visitor.endVisit(this, blockScope);