/*******************************************************************************
- * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Common Public License v0.5
+ * are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v05.html
+ * http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
- ******************************************************************************/
+ *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.ast;
-import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
-import net.sourceforge.phpdt.internal.compiler.codegen.*;
-import net.sourceforge.phpdt.internal.compiler.flow.*;
-import net.sourceforge.phpdt.internal.compiler.lookup.*;
+import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
+import net.sourceforge.phpdt.internal.compiler.codegen.Label;
+import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FinallyFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.flow.InsideSubRoutineFlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
public class TryStatement extends Statement {
BlockScope scope;
public boolean subRoutineCannotReturn = true;
+ public UnconditionalFlowInfo subRoutineInits;
+
// should rename into subRoutineComplete to be set to false by default
ReferenceBinding[] caughtExceptionTypes;
currentScope.methodScope().recordInitializationStates(flowInfo);
if (anyExceptionVariable != null) {
- anyExceptionVariable.used = true;
+ anyExceptionVariable.useFlag = LocalVariableBinding.USED;
}
if (returnAddressVariable != null) {
- returnAddressVariable.used = true;
+ returnAddressVariable.useFlag = LocalVariableBinding.USED;
}
InsideSubRoutineFlowContext insideSubContext;
FinallyFlowContext finallyContext;
} else {
// analyse finally block first
insideSubContext = new InsideSubRoutineFlowContext(flowContext, this);
- subInfo =
+ subInfo =
finallyBlock
.analyseCode(
currentScope,
finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
flowInfo.copy())
.unconditionalInits();
- if (!((subInfo == FlowInfo.DeadEnd) || subInfo.isFakeReachable())) {
+ if (subInfo.isReachable()) {
subRoutineCannotReturn = false;
}
+ this.subRoutineInits = subInfo;
}
// process the try block in a context handling the local exceptions.
ExceptionHandlingFlowContext handlingContext =
tryBlockExit = false;
} else {
tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
- tryBlockExit = (tryInfo == FlowInfo.DeadEnd) || tryInfo.isFakeReachable();
+ tryBlockExit = !tryInfo.isReachable();
}
// check unreachable catch blocks
- handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
+// handlingContext.complainIfUnusedExceptionHandlers(catchBlocks, scope, this);
// process the catch blocks - computing the minimal exit depth amongst try/catch
if (catchArguments != null) {
catchExits = new boolean[catchCount = catchBlocks.length];
for (int i = 0; i < catchCount; i++) {
// keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
- ///*
FlowInfo catchInfo =
flowInfo
.copy()
"(uncheckedExceptionTypes notNil and: [uncheckedExceptionTypes at: index])
ifTrue: [catchInits addPotentialInitializationsFrom: tryInits]."
*/
+ // TODO: should only tag as unreachable if the catchblock cannot be reached
+ //??? if (!handlingContext.initsOnException(caughtExceptionTypes[i]).isReachable()){
if (tryBlock.statements == null) {
- catchInfo.markAsFakeReachable(true);
+ catchInfo.setReachMode(FlowInfo.UNREACHABLE);
}
catchInfo =
catchBlocks[i].analyseCode(
currentScope,
insideSubContext == null ? flowContext : insideSubContext,
catchInfo);
- catchExits[i] =
- ((catchInfo == FlowInfo.DeadEnd) || catchInfo.isFakeReachable());
+ catchExits[i] = !catchInfo.isReachable();
tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
}
}
return tryInfo;
}
+
// we also need to check potential multiple assignments of final variables inside the finally block
// need to include potential inits from returns inside the try/catch parts - 1GK2AOF
- tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn);
- finallyContext.complainOnRedundantFinalAssignments(tryInfo, currentScope);
- if (subInfo == FlowInfo.DeadEnd) {
+ finallyContext.complainOnRedundantFinalAssignments(
+ tryInfo.isReachable()
+ ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
+ : insideSubContext.initsOnReturn,
+ currentScope);
+ if (subInfo == FlowInfo.DEAD_END) {
mergedInitStateIndex =
currentScope.methodScope().recordInitializationStates(subInfo);
return subInfo;
* Try statement code generation
*
*/
- public void generateCode(BlockScope currentScope, CodeStream codeStream) {
-
- if ((bits & IsReachableMASK) == 0) {
- return;
- }
- if (tryBlock.isEmptyBlock()) {
- if (subRoutineStartLabel != null) {
- // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
- finallyBlock.generateCode(scope, codeStream);
- }
- // May loose some local variable initializations : affecting the local variable attributes
- if (mergedInitStateIndex != -1) {
- codeStream.removeNotDefinitelyAssignedVariables(
- currentScope,
- mergedInitStateIndex);
- }
- // no local bytecode produced so no need for position remembering
- return;
- }
- int pc = codeStream.position;
- Label endLabel = new Label(codeStream);
- boolean requiresNaturalJsr = false;
-
- // preparing exception labels
- int maxCatches;
- ExceptionLabel[] exceptionLabels =
- new ExceptionLabel[maxCatches =
- catchArguments == null ? 0 : catchArguments.length];
- for (int i = 0; i < maxCatches; i++) {
- boolean preserveCurrentHandler =
- (preserveExceptionHandler[i
- / ExceptionHandlingFlowContext.BitCacheSize]
- & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
- != 0;
- if (preserveCurrentHandler) {
- exceptionLabels[i] =
- new ExceptionLabel(
- codeStream,
- (ReferenceBinding) catchArguments[i].binding.type);
- }
- }
- ExceptionLabel anyExceptionLabel = null;
- if (subRoutineStartLabel != null) {
- subRoutineStartLabel.codeStream = codeStream;
- anyExceptionLabel = new ExceptionLabel(codeStream, null);
+// public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+//
+// if ((bits & IsReachableMASK) == 0) {
+// return;
+// }
+// if (tryBlock.isEmptyBlock()) {
+// if (subRoutineStartLabel != null) {
+// // since not passing the finallyScope, the block generation will exitUserScope(finallyScope)
+// finallyBlock.generateCode(scope, codeStream);
+// }
+// // May loose some local variable initializations : affecting the local variable attributes
+// if (mergedInitStateIndex != -1) {
+// codeStream.removeNotDefinitelyAssignedVariables(
+// currentScope,
+// mergedInitStateIndex);
+// }
+// // no local bytecode produced so no need for position remembering
+// return;
+// }
+// int pc = codeStream.position;
+// Label endLabel = new Label(codeStream);
+// boolean requiresNaturalJsr = false;
+//
+// // preparing exception labels
+// int maxCatches;
+// ExceptionLabel[] exceptionLabels =
+// new ExceptionLabel[maxCatches =
+// catchArguments == null ? 0 : catchArguments.length];
+// for (int i = 0; i < maxCatches; i++) {
+// boolean preserveCurrentHandler =
+// (preserveExceptionHandler[i
+// / ExceptionHandlingFlowContext.BitCacheSize]
+// & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+// != 0;
+// if (preserveCurrentHandler) {
+// exceptionLabels[i] =
+// new ExceptionLabel(
+// codeStream,
+// (ReferenceBinding) catchArguments[i].binding.type);
+// }
+// }
+// ExceptionLabel anyExceptionLabel = null;
+// if (subRoutineStartLabel != null) {
+// subRoutineStartLabel.codeStream = codeStream;
+// anyExceptionLabel = new ExceptionLabel(codeStream, null);
+// }
+// // generate the try block
+// tryBlock.generateCode(scope, codeStream);
+// boolean tryBlockHasSomeCode = codeStream.position != pc;
+// // flag telling if some bytecodes were issued inside the try block
+//
+// // natural exit: only if necessary
+// boolean nonReturningSubRoutine =
+// (subRoutineStartLabel != null) && subRoutineCannotReturn;
+// if ((!tryBlockExit) && tryBlockHasSomeCode) {
+// int position = codeStream.position;
+// if (nonReturningSubRoutine) {
+// codeStream.goto_(subRoutineStartLabel);
+// } else {
+// requiresNaturalJsr = true;
+// codeStream.goto_(endLabel);
+// }
+// codeStream.updateLastRecordedEndPC(position);
+// //goto is tagged as part of the try block
+// }
+// // place end positions of user-defined exception labels
+// if (tryBlockHasSomeCode) {
+// for (int i = 0; i < maxCatches; i++) {
+// boolean preserveCurrentHandler =
+// (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
+// & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+// != 0;
+// if (preserveCurrentHandler) {
+// exceptionLabels[i].placeEnd();
+// }
+// }
+// /* generate sequence of handler, all starting by storing the TOS (exception
+// thrown) into their own catch variables, the one specified in the source
+// that must denote the handled exception.
+// */
+// if (catchArguments == null) {
+// if (anyExceptionLabel != null) {
+// anyExceptionLabel.placeEnd();
+// }
+// } else {
+// for (int i = 0; i < maxCatches; i++) {
+// boolean preserveCurrentHandler =
+// (preserveExceptionHandler[i / ExceptionHandlingFlowContext.BitCacheSize]
+// & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
+// != 0;
+// if (preserveCurrentHandler) {
+// // May loose some local variable initializations : affecting the local variable attributes
+// if (preTryInitStateIndex != -1) {
+// codeStream.removeNotDefinitelyAssignedVariables(
+// currentScope,
+// preTryInitStateIndex);
+// }
+// exceptionLabels[i].place();
+// codeStream.incrStackSize(1);
+// // optimizing the case where the exception variable is not actually used
+// LocalVariableBinding catchVar;
+// int varPC = codeStream.position;
+// if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
+// codeStream.store(catchVar, false);
+// catchVar.recordInitializationStartPC(codeStream.position);
+// codeStream.addVisibleLocalVariable(catchVar);
+// } else {
+// codeStream.pop();
+// }
+// codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
+// // Keep track of the pcs at diverging point for computing the local attribute
+// // since not passing the catchScope, the block generation will exitUserScope(catchScope)
+// catchBlocks[i].generateCode(scope, codeStream);
+// }
+// if (i == maxCatches - 1) {
+// if (anyExceptionLabel != null) {
+// anyExceptionLabel.placeEnd();
+// }
+// if (subRoutineStartLabel != null) {
+// if (!catchExits[i] && preserveCurrentHandler) {
+// requiresNaturalJsr = true;
+// codeStream.goto_(endLabel);
+// }
+// }
+// } else {
+// if (!catchExits[i] && preserveCurrentHandler) {
+// if (nonReturningSubRoutine) {
+// codeStream.goto_(subRoutineStartLabel);
+// } else {
+// requiresNaturalJsr = true;
+// codeStream.goto_(endLabel);
+// }
+// }
+// }
+// }
+// }
+// // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
+// // inside catch blocks) will run the finally block
+// int finallySequenceStartPC = codeStream.position;
+// if (subRoutineStartLabel != null) {
+// // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
+// anyExceptionLabel.place();
+//
+// if (preTryInitStateIndex != -1) {
+// // reset initialization state, as for a normal catch block
+// codeStream.removeNotDefinitelyAssignedVariables(
+// currentScope,
+// preTryInitStateIndex);
+// }
+//
+// codeStream.incrStackSize(1);
+// if (nonReturningSubRoutine) {
+// codeStream.pop();
+// // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
+// } else {
+// codeStream.store(anyExceptionVariable, false);
+// codeStream.jsr(subRoutineStartLabel);
+// codeStream.load(anyExceptionVariable);
+// codeStream.athrow();
+// }
+// }
+// // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
+// endLabel.place();
+// if (subRoutineStartLabel != null) {
+// if (nonReturningSubRoutine) {
+// requiresNaturalJsr = false;
+// }
+// Label veryEndLabel = new Label(codeStream);
+// if (requiresNaturalJsr) {
+// codeStream.jsr(subRoutineStartLabel);
+// codeStream.goto_(veryEndLabel);
+// }
+// subRoutineStartLabel.place();
+// if (!nonReturningSubRoutine) {
+// codeStream.incrStackSize(1);
+// codeStream.store(returnAddressVariable, false);
+// }
+// codeStream.recordPositionsFrom(
+// finallySequenceStartPC,
+// finallyBlock.sourceStart);
+// // entire sequence for finally is associated to finally block
+// finallyBlock.generateCode(scope, codeStream);
+// if (!nonReturningSubRoutine) {
+// int position = codeStream.position;
+// codeStream.ret(returnAddressVariable.resolvedPosition);
+// codeStream.updateLastRecordedEndPC(position);
+// // the ret bytecode is part of the subroutine
+// }
+// if (requiresNaturalJsr) {
+// veryEndLabel.place();
+// }
+// }
+// } else {
+// // try block had no effect, only generate the body of the finally block if any
+// if (subRoutineStartLabel != null) {
+// finallyBlock.generateCode(scope, codeStream);
+// }
+// }
+// // May loose some local variable initializations : affecting the local variable attributes
+// if (mergedInitStateIndex != -1) {
+// codeStream.removeNotDefinitelyAssignedVariables(
+// currentScope,
+// mergedInitStateIndex);
+// codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
+// }
+// codeStream.recordPositionsFrom(pc, this.sourceStart);
+// }
+
+ public void resetStateForCodeGeneration() {
+ if (this.subRoutineStartLabel != null) {
+ this.subRoutineStartLabel.resetStateForCodeGeneration();
}
- // generate the try block
- tryBlock.generateCode(scope, codeStream);
- boolean tryBlockHasSomeCode = codeStream.position != pc;
- // flag telling if some bytecodes were issued inside the try block
-
- // natural exit: only if necessary
- boolean nonReturningSubRoutine =
- (subRoutineStartLabel != null) && subRoutineCannotReturn;
- if ((!tryBlockExit) && tryBlockHasSomeCode) {
- int position = codeStream.position;
- if (nonReturningSubRoutine) {
- codeStream.goto_(subRoutineStartLabel);
- } else {
- requiresNaturalJsr = true;
- codeStream.goto_(endLabel);
- }
- codeStream.updateLastRecordedEndPC(position);
- //goto is tagged as part of the try block
- }
- // place end positions of user-defined exception labels
- if (tryBlockHasSomeCode) {
- for (int i = 0; i < maxCatches; i++) {
- boolean preserveCurrentHandler =
- (preserveExceptionHandler[i
- / ExceptionHandlingFlowContext.BitCacheSize]
- & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
- != 0;
- if (preserveCurrentHandler) {
- exceptionLabels[i].placeEnd();
- }
- }
- /* generate sequence of handler, all starting by storing the TOS (exception
- thrown) into their own catch variables, the one specified in the source
- that must denote the handled exception.
- */
- if (catchArguments == null) {
- if (anyExceptionLabel != null) {
- anyExceptionLabel.placeEnd();
- }
- } else {
- for (int i = 0; i < maxCatches; i++) {
- boolean preserveCurrentHandler =
- (preserveExceptionHandler[i
- / ExceptionHandlingFlowContext.BitCacheSize]
- & (1 << (i % ExceptionHandlingFlowContext.BitCacheSize)))
- != 0;
- if (preserveCurrentHandler) {
- // May loose some local variable initializations : affecting the local variable attributes
- if (preTryInitStateIndex != -1) {
- codeStream.removeNotDefinitelyAssignedVariables(
- currentScope,
- preTryInitStateIndex);
- }
- exceptionLabels[i].place();
- codeStream.incrStackSize(1);
- // optimizing the case where the exception variable is not actually used
- LocalVariableBinding catchVar;
- int varPC = codeStream.position;
- if ((catchVar = catchArguments[i].binding).resolvedPosition != -1) {
- codeStream.store(catchVar, false);
- catchVar.recordInitializationStartPC(codeStream.position);
- codeStream.addVisibleLocalVariable(catchVar);
- } else {
- codeStream.pop();
- }
- codeStream.recordPositionsFrom(varPC, catchArguments[i].sourceStart);
- // Keep track of the pcs at diverging point for computing the local attribute
- // since not passing the catchScope, the block generation will exitUserScope(catchScope)
- catchBlocks[i].generateCode(scope, codeStream);
- }
- if (i == maxCatches - 1) {
- if (anyExceptionLabel != null) {
- anyExceptionLabel.placeEnd();
- }
- if (subRoutineStartLabel != null) {
- if (!catchExits[i] && preserveCurrentHandler) {
- requiresNaturalJsr = true;
- codeStream.goto_(endLabel);
- }
- }
- } else {
- if (!catchExits[i] && preserveCurrentHandler) {
- if (nonReturningSubRoutine) {
- codeStream.goto_(subRoutineStartLabel);
- } else {
- requiresNaturalJsr = true;
- codeStream.goto_(endLabel);
- }
- }
- }
- }
- }
- // addition of a special handler so as to ensure that any uncaught exception (or exception thrown
- // inside catch blocks) will run the finally block
- int finallySequenceStartPC = codeStream.position;
- if (subRoutineStartLabel != null) {
- // the additional handler is doing: jsr finallyBlock and rethrow TOS-exception
- anyExceptionLabel.place();
-
- if (preTryInitStateIndex != -1) {
- // reset initialization state, as for a normal catch block
- codeStream.removeNotDefinitelyAssignedVariables(
- currentScope,
- preTryInitStateIndex);
- }
+ }
+ public StringBuffer printStatement(int indent, StringBuffer output) {
+ printIndent(indent, output).append("try \n"); //$NON-NLS-1$
+ tryBlock.printStatement(indent + 1, output); //$NON-NLS-1$
- codeStream.incrStackSize(1);
- if (nonReturningSubRoutine) {
- codeStream.pop();
- // "if subroutine cannot return, no need to jsr/jump to subroutine since it will be entered in sequence
- } else {
- codeStream.store(anyExceptionVariable, false);
- codeStream.jsr(subRoutineStartLabel);
- codeStream.load(anyExceptionVariable);
- codeStream.athrow();
- }
- }
- // end of catch sequence, place label that will correspond to the finally block beginning, or end of statement
- endLabel.place();
- if (subRoutineStartLabel != null) {
- if (nonReturningSubRoutine) {
- requiresNaturalJsr = false;
- }
- Label veryEndLabel = new Label(codeStream);
- if (requiresNaturalJsr) {
- codeStream.jsr(subRoutineStartLabel);
- codeStream.goto_(veryEndLabel);
- }
- subRoutineStartLabel.place();
- if (!nonReturningSubRoutine) {
- codeStream.incrStackSize(1);
- codeStream.store(returnAddressVariable, false);
- }
- codeStream.recordPositionsFrom(
- finallySequenceStartPC,
- finallyBlock.sourceStart);
- // entire sequence for finally is associated to finally block
- finallyBlock.generateCode(scope, codeStream);
- if (!nonReturningSubRoutine) {
- int position = codeStream.position;
- codeStream.ret(returnAddressVariable.resolvedPosition);
- codeStream.updateLastRecordedEndPC(position);
- // the ret bytecode is part of the subroutine
- }
- if (requiresNaturalJsr) {
- veryEndLabel.place();
- }
- }
- } else {
- // try block had no effect, only generate the body of the finally block if any
- if (subRoutineStartLabel != null) {
- finallyBlock.generateCode(scope, codeStream);
+ //catches
+ if (catchBlocks != null)
+ for (int i = 0; i < catchBlocks.length; i++) {
+ output.append('\n');
+ printIndent(indent, output).append("catch ("); //$NON-NLS-1$
+ catchArguments[i].print(0, output).append(") "); //$NON-NLS-1$
+ catchBlocks[i].printStatement(indent + 1, output);
}
+ //finally
+ if (finallyBlock != null) {
+ output.append('\n');
+ printIndent(indent, output).append("finally\n"); //$NON-NLS-1$
+ finallyBlock.printStatement(indent + 1, output);
}
- // May loose some local variable initializations : affecting the local variable attributes
- if (mergedInitStateIndex != -1) {
- codeStream.removeNotDefinitelyAssignedVariables(
- currentScope,
- mergedInitStateIndex);
- codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
- }
- codeStream.recordPositionsFrom(pc, this.sourceStart);
- }
+ return output;
+ }
public void resolve(BlockScope upperScope) {
// special scope for secret locals optimization.
// provision for returning and forcing the finally block to run
MethodScope methodScope = scope.methodScope();
- // the type does not matter as long as its not a normal base type
+ // the type does not matter as long as it is not a base type
this.returnAddressVariable =
new LocalVariableBinding(SecretReturnName, upperScope.getJavaLangObject(), AccDefault, false);
finallyScope.addLocalVariable(returnAddressVariable);
for (int i = 0; i < length; i++) {
caughtExceptionTypes[i] = (ReferenceBinding) argumentTypes[i];
for (int j = 0; j < i; j++) {
- if (scope.areTypesCompatible(caughtExceptionTypes[i], argumentTypes[j])) {
+ if (caughtExceptionTypes[i].isCompatibleWith(argumentTypes[j])) {
scope.problemReporter().wrongSequenceOfExceptionTypesError(this, i, j);
- return;
+ // cannot return - since may still proceed if unreachable code is ignored (21203)
}
}
}
}
public void traverse(
- IAbstractSyntaxTreeVisitor visitor,
+ ASTVisitor visitor,
BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
}
visitor.endVisit(this, blockScope);
}
-}
\ No newline at end of file
+}