X-Git-Url: http://secure.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java index 139dbbc..59a804d 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/TryStatement.java @@ -1,19 +1,29 @@ /******************************************************************************* - * 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 { @@ -24,6 +34,8 @@ 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; @@ -60,10 +72,10 @@ public class TryStatement extends Statement { 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; @@ -76,16 +88,17 @@ public class TryStatement extends Statement { } 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 = @@ -102,11 +115,11 @@ public class TryStatement extends Statement { 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) { @@ -114,7 +127,6 @@ public class TryStatement extends Statement { 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() @@ -133,16 +145,17 @@ public class TryStatement extends Statement { "(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()); } } @@ -152,11 +165,15 @@ public class TryStatement extends Statement { 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; @@ -177,216 +194,240 @@ public class TryStatement extends Statement { * 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. @@ -403,7 +444,7 @@ public class TryStatement extends Statement { // 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); @@ -460,9 +501,9 @@ public class TryStatement extends Statement { 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) } } } @@ -506,7 +547,7 @@ public class TryStatement extends Statement { } public void traverse( - IAbstractSyntaxTreeVisitor visitor, + ASTVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { @@ -522,4 +563,4 @@ public class TryStatement extends Statement { } visitor.endVisit(this, blockScope); } -} \ No newline at end of file +}