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;
32 public Block[] catchBlocks;
34 public Argument[] catchArguments;
36 public Block finallyBlock;
40 public boolean subRoutineCannotReturn = true;
42 public UnconditionalFlowInfo subRoutineInits;
44 // should rename into subRoutineComplete to be set to false by default
46 ReferenceBinding[] caughtExceptionTypes;
52 public int[] preserveExceptionHandler;
54 Label subRoutineStartLabel;
56 public LocalVariableBinding anyExceptionVariable, returnAddressVariable,
59 public final static char[] SecretReturnName = " returnAddress".toCharArray(); //$NON-NLS-1$
61 public final static char[] SecretAnyHandlerName = " anyExceptionHandler".toCharArray(); //$NON-NLS-1$
63 public static final char[] SecretLocalDeclarationName = " returnValue".toCharArray(); //$NON-NLS-1$
65 // for local variables table attributes
66 int preTryInitStateIndex = -1;
68 int mergedInitStateIndex = -1;
70 public FlowInfo analyseCode(BlockScope currentScope,
71 FlowContext flowContext, FlowInfo flowInfo) {
73 // Consider the try block and catch block so as to compute the
74 // intersection of initializations and
75 // the minimum exit relative depth amongst all of them. Then consider
76 // the subroutine, and append its
77 // initialization to the try/catch ones, if the subroutine completes
78 // normally. If the subroutine does not
79 // complete, then only keep this result for the rest of the analysis
81 // process the finally block (subroutine) - create a context for the
84 preTryInitStateIndex = currentScope.methodScope()
85 .recordInitializationStates(flowInfo);
87 if (anyExceptionVariable != null) {
88 anyExceptionVariable.useFlag = LocalVariableBinding.USED;
90 if (returnAddressVariable != null) {
91 returnAddressVariable.useFlag = LocalVariableBinding.USED;
93 InsideSubRoutineFlowContext insideSubContext;
94 FinallyFlowContext finallyContext;
95 UnconditionalFlowInfo subInfo;
96 if (subRoutineStartLabel == null) {
98 insideSubContext = null;
99 finallyContext = null;
102 // analyse finally block first
103 insideSubContext = new InsideSubRoutineFlowContext(flowContext,
105 subInfo = finallyBlock.analyseCode(
107 finallyContext = new FinallyFlowContext(flowContext,
108 finallyBlock), flowInfo.copy())
109 .unconditionalInits();
110 if (subInfo.isReachable()) {
111 subRoutineCannotReturn = false;
113 this.subRoutineInits = subInfo;
115 // process the try block in a context handling the local exceptions.
116 ExceptionHandlingFlowContext handlingContext = new ExceptionHandlingFlowContext(
117 insideSubContext == null ? flowContext : insideSubContext,
118 tryBlock, caughtExceptionTypes, scope, flowInfo
119 .unconditionalInits());
122 if (tryBlock.statements == null) {
124 tryBlockExit = false;
126 tryInfo = tryBlock.analyseCode(currentScope, handlingContext,
128 tryBlockExit = !tryInfo.isReachable();
131 // check unreachable catch blocks
132 // handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope,
135 // process the catch blocks - computing the minimal exit depth amongst
137 if (catchArguments != null) {
139 catchExits = new boolean[catchCount = catchBlocks.length];
140 for (int i = 0; i < catchCount; i++) {
141 // keep track of the inits that could potentially have led to
142 // this exception handler (for final assignments diagnosis)
143 FlowInfo catchInfo = flowInfo.copy().unconditionalInits()
144 .addPotentialInitializationsFrom(
145 handlingContext.initsOnException(
146 caughtExceptionTypes[i])
147 .unconditionalInits())
148 .addPotentialInitializationsFrom(
149 tryInfo.unconditionalInits())
150 .addPotentialInitializationsFrom(
151 handlingContext.initsOnReturn);
153 // catch var is always set
154 catchInfo.markAsDefinitelyAssigned(catchArguments[i].binding);
156 * "If we are about to consider an unchecked exception handler,
157 * potential inits may have occured inside the try block that
158 * need to be detected , e.g. try { x = 1; throwSomething();}
159 * catch(Exception e){ x = 2} " "(uncheckedExceptionTypes notNil
160 * and: [uncheckedExceptionTypes at: index]) ifTrue: [catchInits
161 * addPotentialInitializationsFrom: tryInits]."
163 // TODO: should only tag as unreachable if the catchblock cannot
166 // (!handlingContext.initsOnException(caughtExceptionTypes[i]).isReachable()){
167 if (tryBlock.statements == null) {
168 catchInfo.setReachMode(FlowInfo.UNREACHABLE);
170 catchInfo = catchBlocks[i].analyseCode(currentScope,
171 insideSubContext == null ? flowContext
172 : insideSubContext, catchInfo);
173 catchExits[i] = !catchInfo.isReachable();
174 tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
177 if (subRoutineStartLabel == null) {
178 mergedInitStateIndex = currentScope.methodScope()
179 .recordInitializationStates(tryInfo);
183 // we also need to check potential multiple assignments of final
184 // variables inside the finally block
185 // need to include potential inits from returns inside the try/catch
188 .complainOnRedundantFinalAssignments(
189 tryInfo.isReachable() ? (tryInfo
190 .addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
191 : insideSubContext.initsOnReturn, currentScope);
192 if (subInfo == FlowInfo.DEAD_END) {
193 mergedInitStateIndex = currentScope.methodScope()
194 .recordInitializationStates(subInfo);
197 FlowInfo mergedInfo = tryInfo.addInitializationsFrom(subInfo);
198 mergedInitStateIndex = currentScope.methodScope()
199 .recordInitializationStates(mergedInfo);
204 public boolean cannotReturn() {
206 return subRoutineCannotReturn;
210 * Try statement code generation
213 // public void generateCode(BlockScope currentScope, CodeStream codeStream)
216 // if ((bits & IsReachableMASK) == 0) {
219 // if (tryBlock.isEmptyBlock()) {
220 // if (subRoutineStartLabel != null) {
221 // // since not passing the finallyScope, the block generation will
222 // exitUserScope(finallyScope)
223 // finallyBlock.generateCode(scope, codeStream);
225 // // May loose some local variable initializations : affecting the local
226 // variable attributes
227 // if (mergedInitStateIndex != -1) {
228 // codeStream.removeNotDefinitelyAssignedVariables(
230 // mergedInitStateIndex);
232 // // no local bytecode produced so no need for position remembering
235 // int pc = codeStream.position;
236 // Label endLabel = new Label(codeStream);
237 // boolean requiresNaturalJsr = false;
239 // // preparing exception labels
241 // ExceptionLabel[] exceptionLabels =
242 // new ExceptionLabel[maxCatches =
243 // catchArguments == null ? 0 : catchArguments.length];
244 // for (int i = 0; i < maxCatches; i++) {
245 // boolean preserveCurrentHandler =
246 // (preserveExceptionHandler[i
247 // / ExceptionHandlingFlowContext.BitCacheSize]
248 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
250 // if (preserveCurrentHandler) {
251 // exceptionLabels[i] =
252 // new ExceptionLabel(
254 // (ReferenceBinding) catchArguments[i].binding.type);
257 // ExceptionLabel anyExceptionLabel = null;
258 // if (subRoutineStartLabel != null) {
259 // subRoutineStartLabel.codeStream = codeStream;
260 // anyExceptionLabel = new ExceptionLabel(codeStream, null);
262 // // generate the try block
263 // tryBlock.generateCode(scope, codeStream);
264 // boolean tryBlockHasSomeCode = codeStream.position != pc;
265 // // flag telling if some bytecodes were issued inside the try block
267 // // natural exit: only if necessary
268 // boolean nonReturningSubRoutine =
269 // (subRoutineStartLabel != null) && subRoutineCannotReturn;
270 // if ((!tryBlockExit) && tryBlockHasSomeCode) {
271 // int position = codeStream.position;
272 // if (nonReturningSubRoutine) {
273 // codeStream.goto_(subRoutineStartLabel);
275 // requiresNaturalJsr = true;
276 // codeStream.goto_(endLabel);
278 // codeStream.updateLastRecordedEndPC(position);
279 // //goto is tagged as part of the try block
281 // // place end positions of user-defined exception labels
282 // if (tryBlockHasSomeCode) {
283 // for (int i = 0; i < maxCatches; i++) {
284 // boolean preserveCurrentHandler =
285 // (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
286 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
288 // if (preserveCurrentHandler) {
289 // exceptionLabels[i].placeEnd();
292 // /* generate sequence of handler, all starting by storing the TOS
294 // thrown) into their own catch variables, the one specified in the source
295 // that must denote the handled exception.
297 // if (catchArguments == null) {
298 // if (anyExceptionLabel != null) {
299 // anyExceptionLabel.placeEnd();
302 // for (int i = 0; i < maxCatches; i++) {
303 // boolean preserveCurrentHandler =
304 // (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
305 // & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
307 // if (preserveCurrentHandler) {
308 // // May loose some local variable initializations : affecting the local
309 // variable attributes
310 // if (preTryInitStateIndex != -1) {
311 // codeStream.removeNotDefinitelyAssignedVariables(
313 // preTryInitStateIndex);
315 // exceptionLabels[i].place();
316 // codeStream.incrStackSize(1);
317 // // optimizing the case where the exception variable is not actually used
318 // LocalVariableBinding catchVar;
319 // int varPC = codeStream.position;
320 // if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
321 // codeStream.store(catchVar, false);
322 // catchVar.recordInitializationStartPC(codeStream.position);
323 // codeStream.addVisibleLocalVariable(catchVar);
327 // codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
328 // // Keep track of the pcs at diverging point for computing the local
330 // // since not passing the catchScope, the block generation will
331 // exitUserScope(catchScope)
332 // catchBlocks[i].generateCode(scope, codeStream);
334 // if (i == maxCatches - 1) {
335 // if (anyExceptionLabel != null) {
336 // anyExceptionLabel.placeEnd();
338 // if (subRoutineStartLabel != null) {
339 // if (!catchExits[i] && preserveCurrentHandler) {
340 // requiresNaturalJsr = true;
341 // codeStream.goto_(endLabel);
345 // if (!catchExits[i] && preserveCurrentHandler) {
346 // if (nonReturningSubRoutine) {
347 // codeStream.goto_(subRoutineStartLabel);
349 // requiresNaturalJsr = true;
350 // codeStream.goto_(endLabel);
356 // // addition of a special handler so as to ensure that any uncaught
357 // exception (or exception thrown
358 // // inside catch blocks) will run the finally block
359 // int finallySequenceStartPC = codeStream.position;
360 // if (subRoutineStartLabel != null) {
361 // // the additional handler is doing: jsr finallyBlock and rethrow
363 // anyExceptionLabel.place();
365 // if (preTryInitStateIndex != -1) {
366 // // reset initialization state, as for a normal catch block
367 // codeStream.removeNotDefinitelyAssignedVariables(
369 // preTryInitStateIndex);
372 // codeStream.incrStackSize(1);
373 // if (nonReturningSubRoutine) {
375 // // "if subroutine cannot return, no need to jsr/jump to subroutine since
376 // it will be entered in sequence
378 // codeStream.store(anyExceptionVariable, false);
379 // codeStream.jsr(subRoutineStartLabel);
380 // codeStream.load(anyExceptionVariable);
381 // codeStream.athrow();
384 // // end of catch sequence, place label that will correspond to the finally
385 // block beginning, or end of statement
387 // if (subRoutineStartLabel != null) {
388 // if (nonReturningSubRoutine) {
389 // requiresNaturalJsr = false;
391 // Label veryEndLabel = new Label(codeStream);
392 // if (requiresNaturalJsr) {
393 // codeStream.jsr(subRoutineStartLabel);
394 // codeStream.goto_(veryEndLabel);
396 // subRoutineStartLabel.place();
397 // if (!nonReturningSubRoutine) {
398 // codeStream.incrStackSize(1);
399 // codeStream.store(returnAddressVariable, false);
401 // codeStream.recordPositionsFrom(
402 // finallySequenceStartPC,
403 // finallyBlock.sourceStart);
404 // // entire sequence for finally is associated to finally block
405 // finallyBlock.generateCode(scope, codeStream);
406 // if (!nonReturningSubRoutine) {
407 // int position = codeStream.position;
408 // codeStream.ret(returnAddressVariable.resolvedPosition);
409 // codeStream.updateLastRecordedEndPC(position);
410 // // the ret bytecode is part of the subroutine
412 // if (requiresNaturalJsr) {
413 // veryEndLabel.place();
417 // // try block had no effect, only generate the body of the finally block
419 // if (subRoutineStartLabel != null) {
420 // finallyBlock.generateCode(scope, codeStream);
423 // // May loose some local variable initializations : affecting the local
424 // variable attributes
425 // if (mergedInitStateIndex != -1) {
426 // codeStream.removeNotDefinitelyAssignedVariables(
428 // mergedInitStateIndex);
429 // codeStream.addDefinitelyAssignedVariables(currentScope,
430 // mergedInitStateIndex);
432 // codeStream.recordPositionsFrom(pc, this.sourceStart);
434 public void resetStateForCodeGeneration() {
435 if (this.subRoutineStartLabel != null) {
436 this.subRoutineStartLabel.resetStateForCodeGeneration();
440 public StringBuffer printStatement(int indent, StringBuffer output) {
441 printIndent(indent, output).append("try \n"); //$NON-NLS-1$
442 tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
445 if (catchBlocks != null)
446 for (int i = 0; i < catchBlocks.length; i++) {
448 printIndent(indent, output).append("catch ("); //$NON-NLS-1$
449 catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
450 catchBlocks[i].printStatement(indent + 1, output);
453 if (finallyBlock != null) {
455 printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
456 finallyBlock.printStatement(indent + 1, output);
462 public void resolve(BlockScope upperScope) {
464 // special scope for secret locals optimization.
465 this.scope = new BlockScope(upperScope);
467 BlockScope tryScope = new BlockScope(scope);
468 BlockScope finallyScope = null;
470 if (finallyBlock != null && finallyBlock.statements != null) {
472 finallyScope = new BlockScope(scope, false); // don't add it yet
475 // provision for returning and forcing the finally block to run
476 MethodScope methodScope = scope.methodScope();
478 // the type does not matter as long as it is not a base type
479 this.returnAddressVariable = new LocalVariableBinding(
480 SecretReturnName, upperScope.getJavaLangObject(),
482 finallyScope.addLocalVariable(returnAddressVariable);
483 this.returnAddressVariable.constant = NotAConstant; // not inlinable
484 this.subRoutineStartLabel = new Label();
486 this.anyExceptionVariable = new LocalVariableBinding(
487 SecretAnyHandlerName, scope.getJavaLangThrowable(),
489 finallyScope.addLocalVariable(this.anyExceptionVariable);
490 this.anyExceptionVariable.constant = NotAConstant; // not inlinable
492 if (!methodScope.isInsideInitializer()) {
493 MethodBinding methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding;
494 if (methodBinding != null) {
495 TypeBinding methodReturnType = methodBinding.returnType;
496 if (methodReturnType.id != T_void) {
497 this.secretReturnValue = new LocalVariableBinding(
498 SecretLocalDeclarationName, methodReturnType,
500 finallyScope.addLocalVariable(this.secretReturnValue);
501 this.secretReturnValue.constant = NotAConstant; // not
506 finallyBlock.resolveUsing(finallyScope);
507 // force the finally scope to have variable positions shifted after
508 // its try scope and catch ones
509 finallyScope.shiftScopes = new BlockScope[catchArguments == null ? 1
510 : catchArguments.length + 1];
511 finallyScope.shiftScopes[0] = tryScope;
513 this.tryBlock.resolveUsing(tryScope);
515 // arguments type are checked against JavaLangThrowable in
516 // resolveForCatch(..)
517 if (this.catchBlocks != null) {
518 int length = this.catchArguments.length;
519 TypeBinding[] argumentTypes = new TypeBinding[length];
520 for (int i = 0; i < length; i++) {
521 BlockScope catchScope = new BlockScope(scope);
522 if (finallyScope != null) {
523 finallyScope.shiftScopes[i + 1] = catchScope;
525 // side effect on catchScope in resolveForCatch(..)
526 if ((argumentTypes[i] = catchArguments[i]
527 .resolveForCatch(catchScope)) == null)
529 catchBlocks[i].resolveUsing(catchScope);
532 // Verify that the catch clause are ordered in the right way:
533 // more specialized first.
534 this.caughtExceptionTypes = new ReferenceBinding[length];
535 for (int i = 0; i < length; i++) {
536 caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
537 for (int j = 0; j < i; j++) {
538 if (caughtExceptionTypes[i]
539 .isCompatibleWith(argumentTypes[j])) {
540 scope.problemReporter()
541 .wrongSequenceOfExceptionTypesError(this, i, j);
542 // cannot return - since may still proceed if
543 // unreachable code is ignored (21203)
548 caughtExceptionTypes = new ReferenceBinding[0];
551 if (finallyScope != null) {
552 // add finallyScope as last subscope, so it can be shifted behind
553 // try/catch subscopes.
554 // the shifting is necessary to achieve no overlay in between the
555 // finally scope and its
556 // sibling in term of local variable positions.
557 this.scope.addSubscope(finallyScope);
561 public String toString(int tab) {
562 String s = tabString(tab);
564 s = s + "try "; //$NON-NLS-1$
565 if (tryBlock == Block.None)
566 s = s + "{}"; //$NON-NLS-1$
568 s = s + "\n" + tryBlock.toString(tab + 1); //$NON-NLS-1$
571 if (catchBlocks != null)
572 for (int i = 0; i < catchBlocks.length; i++)
573 s = s + "\n" + tabString(tab) + "catch (" //$NON-NLS-2$ //$NON-NLS-1$
574 + catchArguments[i].toString(0) + ") " //$NON-NLS-1$
575 + catchBlocks[i].toString(tab + 1);
577 if (finallyBlock != null) {
578 if (finallyBlock == Block.None)
579 s = s + "\n" + tabString(tab) + "finally {}"; //$NON-NLS-2$ //$NON-NLS-1$
581 s = s + "\n" + tabString(tab) + "finally\n" + //$NON-NLS-2$ //$NON-NLS-1$
582 finallyBlock.toString(tab + 1);
588 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
590 if (visitor.visit(this, blockScope)) {
591 tryBlock.traverse(visitor, scope);
592 if (catchArguments != null) {
593 for (int i = 0, max = catchBlocks.length; i < max; i++) {
594 catchArguments[i].traverse(visitor, scope);
595 catchBlocks[i].traverse(visitor, scope);
598 if (finallyBlock != null)
599 finallyBlock.traverse(visitor, scope);
601 visitor.endVisit(this, blockScope);