Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 30700 Details for
Bug 110030
[plan][compiler] Provide support for null reference analysis
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Null checks stage1: provides checks upon locals.
org.eclipse.jdt.core_124_v_623.txt (text/plain), 200.49 KB, created by
Maxime Daniel
on 2005-11-28 07:15:33 EST
(
hide
)
Description:
Null checks stage1: provides checks upon locals.
Filename:
MIME Type:
Creator:
Maxime Daniel
Created:
2005-11-28 07:15:33 EST
Size:
200.49 KB
patch
obsolete
>Index: compiler/org/eclipse/jdt/core/compiler/IProblem.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java,v >retrieving revision 1.161 >diff -u -r1.161 IProblem.java >--- compiler/org/eclipse/jdt/core/compiler/IProblem.java 21 Oct 2005 07:19:17 -0000 1.161 >+++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 28 Nov 2005 12:08:04 -0000 >@@ -684,6 +684,8 @@ > int LocalVariableCannotBeNull = MethodRelated + 397; > /** @since 3.1 */ > int LocalVariableCanOnlyBeNull = MethodRelated + 398; >+ /** @since 3.2 */ >+ int LocalVariableMayBeNull = MethodRelated + 399; > > // method verifier problems > int AbstractMethodMustBeImplemented = MethodRelated + 400; >Index: compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java,v >retrieving revision 1.31 >diff -u -r1.31 AND_AND_Expression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java 18 Nov 2005 16:46:22 -0000 1.31 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java 28 Nov 2005 12:08:04 -0000 >@@ -49,7 +49,7 @@ > // need to be careful of scenario: > // (x && y) && !z, if passing the left info to the right, it would be > // swapped by the ! >- FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalInits().copy(); >+ FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalCopy(); > rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo); > > int previousMode = rightInfo.reachMode(); >@@ -57,12 +57,11 @@ > rightInfo.setReachMode(FlowInfo.UNREACHABLE); > } > rightInfo = right.analyseCode(currentScope, flowContext, rightInfo); >- FlowInfo trueMergedInfo = rightInfo.initsWhenTrue().copy(); >- rightInfo.setReachMode(previousMode); // reset after trueMergedInfo got extracted > FlowInfo mergedInfo = FlowInfo.conditional( >- trueMergedInfo, >- leftInfo.initsWhenFalse().copy().unconditionalInits().mergedWith( >- rightInfo.initsWhenFalse().copy().unconditionalInits())); >+ rightInfo.safeInitsWhenTrue(), >+ leftInfo.initsWhenFalse().unconditionalInits().mergedWith( >+ rightInfo.initsWhenFalse().setReachMode(previousMode).unconditionalInits())); >+ // reset after trueMergedInfo got extracted > mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); > return mergedInfo; > } >Index: compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java,v >retrieving revision 1.38 >diff -u -r1.38 ArrayReference.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 10 Oct 2005 10:29:38 -0000 1.38 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 28 Nov 2005 12:08:04 -0000 >@@ -27,34 +27,32 @@ > sourceStart = rec.sourceStart; > } > >- public FlowInfo analyseAssignment( >+public FlowInfo analyseAssignment( > BlockScope currentScope, > FlowContext flowContext, > FlowInfo flowInfo, > Assignment assignment, > boolean compoundAssignment) { >+ // unconditionalInits is applied to all existing calls -> we remove extraneous ones here >+ if (assignment.expression == null) { >+ return analyseCode(currentScope, flowContext, flowInfo); >+ } >+ return assignment >+ .expression >+ .analyseCode( >+ currentScope, >+ flowContext, >+ analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()); >+} > >- if (assignment.expression == null) { >- return analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); >- } >- return assignment >- .expression >- .analyseCode( >- currentScope, >- flowContext, >- analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()) >- .unconditionalInits(); >- } >- >- public FlowInfo analyseCode( >+public FlowInfo analyseCode( > BlockScope currentScope, > FlowContext flowContext, > FlowInfo flowInfo) { >- >- flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); >- receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >- return position.analyseCode(currentScope, flowContext, flowInfo); >- } >+ receiver.checkNPE(currentScope, flowContext, flowInfo, true); >+ flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo); >+ return position.analyseCode(currentScope, flowContext, flowInfo); >+} > > public void generateAssignment( > BlockScope currentScope, >@@ -179,6 +177,10 @@ > codeStream.arrayAtPut(this.resolvedType.id, false); > } > >+public int nullStatus(FlowInfo flowInfo) { >+ return FlowInfo.UNKNOWN; >+} >+ > public StringBuffer printExpression(int indent, StringBuffer output) { > > receiver.printExpression(0, output).append('['); >Index: compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java,v >retrieving revision 1.44 >diff -u -r1.44 AssertStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 18 Nov 2005 16:46:22 -0000 1.44 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 28 Nov 2005 12:08:04 -0000 >@@ -54,11 +54,12 @@ > boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; > boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; > >- FlowInfo assertInfo = flowInfo.copy(); >+ UnconditionalFlowInfo assertInfo = assertExpression. >+ analyseCode(currentScope, flowContext, flowInfo.copy()). >+ unconditionalInits(); > if (isOptimizedTrueAssertion) { > assertInfo.setReachMode(FlowInfo.UNREACHABLE); > } >- assertInfo = assertExpression.analyseCode(currentScope, flowContext, assertInfo).unconditionalInits(); > > if (exceptionArgument != null) { > // only gets evaluated when escaping - results are not taken into account >@@ -80,7 +81,7 @@ > if (isOptimizedFalseAssertion) { > return flowInfo; // if assertions are enabled, the following code will be unreachable > } else { >- return flowInfo.mergedWith(assertInfo.unconditionalInits()); >+ return flowInfo.mergedWith(assertInfo); > } > } > >Index: compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java,v >retrieving revision 1.66 >diff -u -r1.66 Assignment.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 18 Nov 2005 16:46:22 -0000 1.66 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 28 Nov 2005 12:08:04 -0000 >@@ -36,34 +36,38 @@ > this.sourceEnd = sourceEnd; > } > >- public FlowInfo analyseCode( >- BlockScope currentScope, >- FlowContext flowContext, >- FlowInfo flowInfo) { >- // record setting a variable: various scenarii are possible, setting an array reference, >- // a field reference, a blank final field reference, a field of an enclosing instance or >- // just a local variable. >- >- LocalVariableBinding local = this.lhs.localVariableBinding(); >- int nullStatus = this.expression.nullStatus(flowInfo); >- if (local != null && nullStatus == FlowInfo.NULL) { >- flowContext.recordUsingNullReference(currentScope, local, this.lhs, FlowInfo.NON_NULL, flowInfo); >- } >- flowInfo = ((Reference) lhs) >- .analyseAssignment(currentScope, flowContext, flowInfo, this, false) >- .unconditionalInits(); >- if (local != null) { >- switch(nullStatus) { >- case FlowInfo.NULL : >- flowInfo.markAsDefinitelyNull(local); >- break; >- case FlowInfo.NON_NULL : >- flowInfo.markAsDefinitelyNonNull(local); >- break; >- } >- } >- return flowInfo; >- } >+public FlowInfo analyseCode( >+ BlockScope currentScope, >+ FlowContext flowContext, >+ FlowInfo flowInfo) { >+ // record setting a variable: various scenarii are possible, setting an array reference, >+ // a field reference, a blank final field reference, a field of an enclosing instance or >+ // just a local variable. >+ LocalVariableBinding local = this.lhs.localVariableBinding(); >+ int nullStatus = this.expression.nullStatus(flowInfo); >+ if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { >+ if (nullStatus == FlowInfo.NULL) { >+ flowContext.recordUsingNullReference(currentScope, local, this.lhs, >+ FlowContext.CAN_ONLY_NULL, flowInfo); >+ } >+ } >+ flowInfo = ((Reference) lhs) >+ .analyseAssignment(currentScope, flowContext, flowInfo, this, false) >+ .unconditionalInits(); >+ if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { >+ switch(nullStatus) { >+ case FlowInfo.NULL : >+ flowInfo.markAsDefinitelyNull(local); >+ break; >+ case FlowInfo.NON_NULL : >+ flowInfo.markAsDefinitelyNonNull(local); >+ break; >+ default: >+ flowInfo.markAsDefinitelyUnknown(local); >+ } >+ } >+ return flowInfo; >+} > > void checkAssignmentEffect(BlockScope scope) { > >@@ -250,4 +254,8 @@ > } > visitor.endVisit(this, scope); > } >+ >+public LocalVariableBinding localVariableBinding() { >+ return lhs.localVariableBinding(); >+} > } >Index: compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java,v >retrieving revision 1.52 >diff -u -r1.52 BinaryExpression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 18 Nov 2005 16:46:21 -0000 1.52 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 28 Nov 2005 12:08:05 -0000 >@@ -31,18 +31,17 @@ > this.sourceEnd = right.sourceEnd; > } > >- public FlowInfo analyseCode( >+public FlowInfo analyseCode( > BlockScope currentScope, > FlowContext flowContext, > FlowInfo flowInfo) { >- >- return right >- .analyseCode( >- currentScope, >- flowContext, >- left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()) >- .unconditionalInits(); >- } >+ left.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); >+ flowInfo = left.analyseCode(currentScope, flowContext, flowInfo). >+ unconditionalInits(); >+ right.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); >+ return right.analyseCode(currentScope, flowContext, flowInfo). >+ unconditionalInits(); >+} > > public void computeConstant(BlockScope scope, int leftId, int rightId) { > >Index: compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java,v >retrieving revision 1.51 >diff -u -r1.51 CompoundAssignment.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 18 Nov 2005 16:46:21 -0000 1.51 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java 28 Nov 2005 12:08:05 -0000 >@@ -34,13 +34,14 @@ > this.operator = operator ; > } > >- public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { >- // record setting a variable: various scenarii are possible, setting an array reference, >- // a field reference, a blank final field reference, a field of an enclosing instance or >- // just a local variable. >- >- return ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits(); >- } >+public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, >+ FlowInfo flowInfo) { >+ // record setting a variable: various scenarii are possible, setting an array reference, >+ // a field reference, a blank final field reference, a field of an enclosing instance or >+ // just a local variable. >+ lhs.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */); >+ return ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits(); >+} > > public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { > >@@ -56,9 +57,10 @@ > codeStream.recordPositionsFrom(pc, this.sourceStart); > } > >- public int nullStatus(FlowInfo flowInfo) { >- return FlowInfo.NON_NULL; >- } >+public int nullStatus(FlowInfo flowInfo) { >+ return FlowInfo.NON_NULL; >+ // we may have complained on checkNPE, but we avoid duplicate error >+} > > public String operatorToString() { > switch (operator) { >Index: compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java,v >retrieving revision 1.75 >diff -u -r1.75 ConditionalExpression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 18 Nov 2005 16:46:21 -0000 1.75 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 28 Nov 2005 12:08:05 -0000 >@@ -40,11 +40,8 @@ > sourceEnd = valueIfFalse.sourceEnd; > } > >- public FlowInfo analyseCode( >- BlockScope currentScope, >- FlowContext flowContext, >+public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, > FlowInfo flowInfo) { >- > Constant cst = this.condition.optimizedBooleanConstant(); > boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; > boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; >@@ -84,16 +81,13 @@ > boolean isValueIfFalseOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true; > boolean isValueIfFalseOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false; > >- UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits(); >+ UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().unconditionalCopy(); >+ UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().unconditionalCopy(); >+ UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().unconditionalInits(); >+ UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().unconditionalInits(); > if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); >- >- UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits(); > if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); >- >- UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits(); > if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); >- >- UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits(); > if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); > > mergedInfo = >@@ -262,6 +256,23 @@ > // no implicit conversion for boolean values > codeStream.updateLastRecordedEndPC(currentScope, codeStream.position); > } >+ >+public int nullStatus(FlowInfo flowInfo) { >+ Constant cst = this.condition.optimizedBooleanConstant(); >+ if (cst != Constant.NotAConstant) { >+ if (cst.booleanValue()) { >+ return valueIfTrue.nullStatus(flowInfo); >+ } >+ return valueIfFalse.nullStatus(flowInfo); >+ } >+ int ifTrueNullStatus = valueIfTrue.nullStatus(flowInfo), >+ ifFalseNullStatus = valueIfFalse.nullStatus(flowInfo); >+ if (ifTrueNullStatus == ifFalseNullStatus) { >+ return ifTrueNullStatus; >+ } >+ return FlowInfo.UNKNOWN; >+ // cannot decide which branch to take, and they disagree >+} > > public Constant optimizedBooleanConstant() { > >Index: compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java,v >retrieving revision 1.6 >diff -u -r1.6 ContinueStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java 14 Oct 2005 22:26:02 -0000 1.6 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java 28 Nov 2005 12:08:05 -0000 >@@ -71,7 +71,7 @@ > flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits > } else if (traversedContext == targetContext) { > // only record continue info once accumulated through subroutines, and only against target context >- targetContext.recordContinueFrom(flowInfo); >+ targetContext.recordContinueFrom(flowContext, flowInfo); > break; > } > } while ((traversedContext = traversedContext.parent) != null); >Index: compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java,v >retrieving revision 1.42 >diff -u -r1.42 DoStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 18 Nov 2005 16:46:21 -0000 1.42 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 28 Nov 2005 12:08:05 -0000 >@@ -46,6 +46,7 @@ > LoopingFlowContext loopingContext = > new LoopingFlowContext( > flowContext, >+ flowInfo, > this, > breakLabel, > continueLabel, >@@ -59,9 +60,14 @@ > > int previousMode = flowInfo.reachMode(); > >- FlowInfo actionInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); >+ UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy(); >+ // we need to collect the contribution to nulls of the coming paths through the >+ // loop, be they falling through normally or branched to break, continue labels >+ // or catch blocks > if ((action != null) && !action.isEmptyBlock()) { >- actionInfo = action.analyseCode(currentScope, loopingContext, actionInfo); >+ actionInfo = action. >+ analyseCode(currentScope, loopingContext, actionInfo). >+ unconditionalInits(); > > // code generation can be optimized when no need to continue in the loop > if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { >@@ -75,22 +81,34 @@ > */ > actionInfo.setReachMode(previousMode); > >- actionInfo = >+ LoopingFlowContext condLoopContext; >+ FlowInfo condInfo = > condition.analyseCode( > currentScope, >- loopingContext, >+ (condLoopContext = >+ new LoopingFlowContext(flowContext, flowInfo, this, null, >+ null, currentScope)), > (action == null > ? actionInfo >- : (actionInfo.mergedWith(loopingContext.initsOnContinue)))); >+ : (actionInfo.mergedWith(loopingContext.initsOnContinue))).copy()); > if (!isConditionOptimizedFalse && continueLabel != null) { >- loopingContext.complainOnDeferredChecks(currentScope, actionInfo); >+ loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo); >+ condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo); >+ UnconditionalFlowInfo checkFlowInfo; >+ loopingContext.complainOnDeferredNullChecks(currentScope, >+ checkFlowInfo = actionInfo. >+ addPotentialNullInfoFrom( >+ condInfo.initsWhenTrue().unconditionalInits())); >+ condLoopContext.complainOnDeferredNullChecks(currentScope, >+ checkFlowInfo); > } > > // end of loop > FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( > loopingContext.initsOnBreak, >- isConditionOptimizedTrue, >- actionInfo.initsWhenFalse().addInitializationsFrom(flowInfo), // recover null inits from before condition analysis >+ isConditionOptimizedTrue, >+ condInfo.isReachable() ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse()) : condInfo, >+ // recover null inits from before condition analysis > false, // never consider opt false case for DO loop, since break can always occur (47776) > !isConditionTrue /*do{}while(true); unreachable(); */); > mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); >Index: compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java,v >retrieving revision 1.57 >diff -u -r1.57 EqualExpression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 18 Nov 2005 16:46:22 -0000 1.57 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 28 Nov 2005 12:08:05 -0000 >@@ -22,84 +22,98 @@ > public EqualExpression(Expression left, Expression right,int operator) { > super(left,right,operator); > } >- public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { >+ private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { > > LocalVariableBinding local = this.left.localVariableBinding(); >- if (local != null) { >+ if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { > checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left); > } > local = this.right.localVariableBinding(); >- if (local != null) { >+ if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { > checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right); > } > } > private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) { > switch (nullStatus) { > case FlowInfo.NULL : >- flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NULL, flowInfo); >+ flowContext.recordUsingNullReference(scope, local, reference, >+ FlowContext.CAN_ONLY_NULL_NON_NULL, flowInfo); > if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { >- initsWhenTrue.markAsDefinitelyNull(local); // from thereon it is set >- initsWhenFalse.markAsDefinitelyNonNull(local); // from thereon it is set >+ initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set >+ initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set > } else { >- initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set >- initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set >+ initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set >+ initsWhenFalse.markAsComparedEqualToNull(local); // from thereon it is set > } > break; > case FlowInfo.NON_NULL : >- flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NON_NULL, flowInfo); >+ flowContext.recordUsingNullReference(scope, local, reference, >+ FlowContext.CAN_ONLY_NULL, flowInfo); > if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { >- initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set >+ initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set > } > break; > } > } > > public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { >+ FlowInfo result; > if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { > if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { > if (left.constant.booleanValue()) { // true == anything > // this is equivalent to the right argument inits >- return right.analyseCode(currentScope, flowContext, flowInfo); >+ result = right.analyseCode(currentScope, flowContext, flowInfo); > } else { // false == anything > // this is equivalent to the right argument inits negated >- return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); >+ result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); > } >- } >- if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { >+ } >+ else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { > if (right.constant.booleanValue()) { // anything == true >- // this is equivalent to the right argument inits >- return left.analyseCode(currentScope, flowContext, flowInfo); >+ // this is equivalent to the left argument inits >+ result = left.analyseCode(currentScope, flowContext, flowInfo); > } else { // anything == false > // this is equivalent to the right argument inits negated >- return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); >+ result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); > } >+ } >+ else { >+ result = right.analyseCode( >+ currentScope, flowContext, >+ left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); > } >- return right.analyseCode( >- currentScope, flowContext, >- left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); > } else { //NOT_EQUAL : > if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { > if (!left.constant.booleanValue()) { // false != anything > // this is equivalent to the right argument inits >- return right.analyseCode(currentScope, flowContext, flowInfo); >+ result = right.analyseCode(currentScope, flowContext, flowInfo); > } else { // true != anything > // this is equivalent to the right argument inits negated >- return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); >+ result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); > } > } >- if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { >+ else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { > if (!right.constant.booleanValue()) { // anything != false > // this is equivalent to the right argument inits >- return left.analyseCode(currentScope, flowContext, flowInfo); >+ result = left.analyseCode(currentScope, flowContext, flowInfo); > } else { // anything != true > // this is equivalent to the right argument inits negated >- return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); >+ result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); > } >+ } >+ else { >+ result = right.analyseCode( >+ currentScope, flowContext, >+ left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()). >+ /* unneeded since we flatten it: asNegatedCondition(). */ >+ unconditionalInits(); > } >- return right.analyseCode( >- currentScope, flowContext, >- left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits(); > } >+ if (result instanceof UnconditionalFlowInfo && result.isReachable()) { // the flow info is flat >+ result = FlowInfo.conditional(result, result.copy()); >+ } >+ checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse()); >+ return result; > } > > public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { >Index: compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java,v >retrieving revision 1.88 >diff -u -r1.88 Expression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 18 Nov 2005 16:46:23 -0000 1.88 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 28 Nov 2005 12:08:05 -0000 >@@ -479,25 +479,28 @@ > } > } > >- public FlowInfo checkNullStatus(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int nullStatus) { >- >- LocalVariableBinding local = this.localVariableBinding(); >- if (local != null) { >- switch(nullStatus) { >- case FlowInfo.NULL : >- flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NULL, flowInfo); >- flowInfo.markAsDefinitelyNull(local); // from thereon it is set >- break; >- case FlowInfo.NON_NULL : >- flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NON_NULL, flowInfo); >- flowInfo.markAsDefinitelyNonNull(local); // from thereon it is set >- break; >- case FlowInfo.UNKNOWN : >- break; >- } >- } >- return flowInfo; >+/** >+ * Check the local variable of this expression, if any, against potential NPEs >+ * given a flow context and an upstream flow info. If so, report the risk to >+ * the context. Marks the local as checked, which affects the flow info. >+ * @param scope the scope of the analysis >+ * @param flowContext the current flow context >+ * @param flowInfo the upstream flow info; caveat: may get modified >+ * @param checkString if true, a local variable of type String is checked; else >+ * it is skipped >+ */ >+public void checkNPE(BlockScope scope, FlowContext flowContext, >+ FlowInfo flowInfo, boolean checkString) { >+ LocalVariableBinding local = this.localVariableBinding(); >+ if (local != null && >+ (local.type.tagBits & TagBits.IsBaseType) == 0 && >+ (checkString || local.type.id != T_JavaLangString)) { >+ flowContext.recordUsingNullReference(scope, local, this, >+ FlowContext.MAY_NULL, flowInfo); >+ flowInfo.markAsComparedEqualToNonNull(local); >+ // from thereon it is set > } >+} > > private MethodBinding[] getAllInheritedMethods(ReferenceBinding binding) { > ArrayList collector = new ArrayList(); >@@ -516,9 +519,6 @@ > getAllInheritedMethods0(superInterfaces[i], collector); > } > } >- public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { >- // do nothing by default - see EqualExpression >- } > > public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { > if (match == castType) { >Index: compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java,v >retrieving revision 1.92 >diff -u -r1.92 FieldReference.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 18 Nov 2005 16:46:23 -0000 1.92 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 28 Nov 2005 12:08:05 -0000 >@@ -98,7 +98,9 @@ > public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { > boolean nonStatic = !binding.isStatic(); > receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic); >- if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >+ if (nonStatic) { >+ receiver.checkNPE(currentScope, flowContext, flowInfo, true); >+ } > > if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { > manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); >@@ -407,6 +409,10 @@ > } > } > >+public int nullStatus(FlowInfo flowInfo) { >+ return FlowInfo.UNKNOWN; >+} >+ > public Constant optimizedBooleanConstant() { > switch (this.resolvedType.id) { > case T_boolean : >Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java,v >retrieving revision 1.51 >diff -u -r1.51 ForStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 18 Nov 2005 16:46:21 -0000 1.51 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 28 Nov 2005 12:08:05 -0000 >@@ -83,38 +83,46 @@ > > // process the condition > LoopingFlowContext condLoopContext = null; >- FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); >+ FlowInfo condInfo = flowInfo.nullInfoLessUnconditionalCopy(); > if (condition != null) { > if (!isConditionTrue) { > condInfo = > condition.analyseCode( > scope, > (condLoopContext = >- new LoopingFlowContext(flowContext, this, null, null, scope)), >+ new LoopingFlowContext(flowContext, flowInfo, this, null, >+ null, scope)), > condInfo); > } > } > > // process the action > LoopingFlowContext loopingContext; >- FlowInfo actionInfo; >+ UnconditionalFlowInfo actionInfo; > if (action == null > || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) { > if (condLoopContext != null) >- condLoopContext.complainOnDeferredChecks(scope, condInfo); >+ condLoopContext.complainOnDeferredFinalChecks(scope, condInfo); > if (isConditionTrue) { >+ if (condLoopContext != null) { >+ condLoopContext.complainOnDeferredNullChecks(currentScope, >+ condInfo); >+ } > return FlowInfo.DEAD_END; > } else { > if (isConditionFalse){ > continueLabel = null; // for(;false;p()); > } >- actionInfo = condInfo.initsWhenTrue().copy().unconditionalInits().discardNullRelatedInitializations(); >+ actionInfo = condInfo.initsWhenTrue().unconditionalCopy(); > loopingContext = >- new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); >+ new LoopingFlowContext(flowContext, flowInfo, this, >+ breakLabel, continueLabel, scope); > } >- } else { >+ } >+ else { > loopingContext = >- new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); >+ new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, >+ continueLabel, scope); > FlowInfo initsWhenTrue = condInfo.initsWhenTrue(); > condIfTrueInitStateIndex = > currentScope.methodScope().recordInitializationStates(initsWhenTrue); >@@ -122,38 +130,63 @@ > if (isConditionFalse) { > actionInfo = FlowInfo.DEAD_END; > } else { >- actionInfo = initsWhenTrue.copy().unconditionalInits().discardNullRelatedInitializations(); >+ actionInfo = initsWhenTrue.unconditionalCopy(); > if (isConditionOptimizedFalse){ > actionInfo.setReachMode(FlowInfo.UNREACHABLE); > } > } > if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { >- actionInfo = action.analyseCode(scope, loopingContext, actionInfo); >+ actionInfo = action.analyseCode(scope, loopingContext, actionInfo). >+ unconditionalInits(); > } > > // code generation can be optimized when no need to continue in the loop > if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { > continueLabel = null; >- } else { >- if (condLoopContext != null) >- condLoopContext.complainOnDeferredChecks(scope, condInfo); >- actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); >- loopingContext.complainOnDeferredChecks(scope, actionInfo); >+ } >+ else { >+ if (condLoopContext != null) { >+ condLoopContext.complainOnDeferredFinalChecks(scope, >+ condInfo); >+ } >+ actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); >+ loopingContext.complainOnDeferredFinalChecks(scope, >+ actionInfo); > } > } > // for increments >- FlowInfo exitBranch = condInfo.initsWhenFalse(); >- exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis >+ FlowInfo exitBranch = flowInfo.copy(); >+ // recover null inits from before condition analysis >+ LoopingFlowContext incrementContext = null; > if (continueLabel != null) { > if (increments != null) { >- LoopingFlowContext loopContext = >- new LoopingFlowContext(flowContext, this, null, null, scope); >+ incrementContext = >+ new LoopingFlowContext(flowContext, flowInfo, this, null, >+ null, scope); >+ FlowInfo incrementInfo = actionInfo; > for (int i = 0, count = increments.length; i < count; i++) { >- actionInfo = increments[i].analyseCode(scope, loopContext, actionInfo); >+ incrementInfo = increments[i]. >+ analyseCode(scope, incrementContext, incrementInfo); > } >- loopContext.complainOnDeferredChecks(scope, actionInfo); >+ incrementContext.complainOnDeferredFinalChecks(scope, >+ actionInfo = incrementInfo.unconditionalInits()); > } >- exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); >+ exitBranch.addPotentialInitializationsFrom(actionInfo). >+ addInitializationsFrom(condInfo.initsWhenFalse()); >+ } >+ else { >+ exitBranch.addInitializationsFrom(condInfo.initsWhenFalse()); >+ } >+ // nulls checks >+ if (condLoopContext != null) { >+ condLoopContext.complainOnDeferredNullChecks(currentScope, >+ actionInfo); >+ } >+ loopingContext.complainOnDeferredNullChecks(currentScope, >+ actionInfo); >+ if (incrementContext != null) { >+ incrementContext.complainOnDeferredNullChecks(currentScope, >+ actionInfo); > } > > //end of loop >Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java,v >retrieving revision 1.25 >diff -u -r1.25 ForeachStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 18 Nov 2005 16:46:22 -0000 1.25 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 28 Nov 2005 12:08:05 -0000 >@@ -17,6 +17,7 @@ > import org.eclipse.jdt.internal.compiler.flow.FlowContext; > import org.eclipse.jdt.internal.compiler.flow.FlowInfo; > import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext; >+import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; > import org.eclipse.jdt.internal.compiler.impl.Constant; > import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; > import org.eclipse.jdt.internal.compiler.lookup.Binding; >@@ -81,9 +82,9 @@ > continueLabel = new Label(); > > // process the element variable and collection >+ this.collection.checkNPE(currentScope, flowContext, flowInfo, true); > flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo); >- FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); >- condInfo = this.collection.analyseCode(scope, flowContext, condInfo); >+ FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy()); > > // element variable will be assigned when iterating > condInfo.markAsDefinitelyAssigned(this.elementVariable.binding); >@@ -91,25 +92,30 @@ > this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo); > > // process the action >- LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope); >- FlowInfo actionInfo = condInfo.initsWhenTrue().copy(); >+ LoopingFlowContext loopingContext = >+ new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, >+ continueLabel, scope); >+ UnconditionalFlowInfo actionInfo = >+ condInfo.nullInfoLessUnconditionalCopy(); > FlowInfo exitBranch; > if (!(action == null || (action.isEmptyBlock() > && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) { > > if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { >- actionInfo = action.analyseCode(scope, loopingContext, actionInfo); >+ actionInfo = action. >+ analyseCode(scope, loopingContext, actionInfo). >+ unconditionalCopy(); > } > > // code generation can be optimized when no need to continue in the loop >- exitBranch = condInfo.initsWhenFalse(); >- exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis >+ exitBranch = flowInfo.unconditionalCopy(). >+ addInitializationsFrom(condInfo.initsWhenFalse()); > if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { > continueLabel = null; > } else { >- actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); >- loopingContext.complainOnDeferredChecks(scope, actionInfo); >- exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); >+ actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); >+ loopingContext.complainOnDeferredFinalChecks(scope, actionInfo); >+ exitBranch.addPotentialInitializationsFrom(actionInfo); > } > } else { > exitBranch = condInfo.initsWhenFalse(); >@@ -133,6 +139,8 @@ > } > } > //end of loop >+ loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo); >+ > FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( > loopingContext.initsOnBreak, > false, >Index: compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java,v >retrieving revision 1.50 >diff -u -r1.50 IfStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 18 Nov 2005 16:46:21 -0000 1.50 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 28 Nov 2005 12:08:05 -0000 >@@ -60,22 +60,22 @@ > FlowInfo flowInfo) { > > // process the condition >- flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo); >+ FlowInfo conditionFlowInfo = >+ condition.analyseCode(currentScope, flowContext, flowInfo); > > Constant cst = this.condition.optimizedBooleanConstant(); > boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; > boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; > > // process the THEN part >- FlowInfo thenFlowInfo = flowInfo.initsWhenTrue().copy(); >+ FlowInfo thenFlowInfo = conditionFlowInfo.safeInitsWhenTrue(); > if (isConditionOptimizedFalse) { > thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); > } >- FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy(); >+ FlowInfo elseFlowInfo = conditionFlowInfo.initsWhenFalse(); > if (isConditionOptimizedTrue) { > elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); > } >- this.condition.checkNullComparison(currentScope, flowContext, flowInfo, thenFlowInfo, elseFlowInfo); > if (this.thenStatement != null) { > // Save info for code gen > thenInitStateIndex = >@@ -107,11 +107,11 @@ > > // merge THEN & ELSE initializations > FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( >- thenFlowInfo, >- isConditionOptimizedTrue, >- elseFlowInfo, >- isConditionOptimizedFalse, >- true /*if(true){ return; } fake-reachable(); */); >+ thenFlowInfo, >+ isConditionOptimizedTrue, >+ elseFlowInfo, >+ isConditionOptimizedFalse, >+ true /*if(true){ return; } fake-reachable(); */); > mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); > return mergedInfo; > } >Index: compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java,v >retrieving revision 1.44 >diff -u -r1.44 InstanceOfExpression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 18 Nov 2005 16:46:21 -0000 1.44 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 28 Nov 2005 12:08:05 -0000 >@@ -31,17 +31,18 @@ > this.sourceEnd = type.sourceEnd; > } > >- public FlowInfo analyseCode( >+public FlowInfo analyseCode( > BlockScope currentScope, > FlowContext flowContext, > FlowInfo flowInfo) { >- >- flowInfo = expression >- .analyseCode(currentScope, flowContext, flowInfo) >- .unconditionalInits(); >- expression.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >- return flowInfo; >+ LocalVariableBinding local = this.expression.localVariableBinding(); >+ if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { >+ flowContext.recordUsingNullReference(currentScope, local, >+ this.expression, FlowContext.CAN_ONLY_NULL, flowInfo); > } >+ return expression.analyseCode(currentScope, flowContext, flowInfo). >+ unconditionalInits(); >+} > > /** > * Code generation for instanceOfExpression >Index: compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java,v >retrieving revision 1.52 >diff -u -r1.52 LocalDeclaration.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 18 Nov 2005 16:46:23 -0000 1.52 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 28 Nov 2005 12:08:05 -0000 >@@ -32,25 +32,22 @@ > this.declarationEnd = sourceEnd; > } > >- public FlowInfo analyseCode( >- BlockScope currentScope, >- FlowContext flowContext, >+public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, > FlowInfo flowInfo) { >- >- // record variable initialization if any >- if (flowInfo.isReachable()) { >- bits |= IsLocalDeclarationReachable; // only set if actually reached >- } >- if (this.initialization == null) >- return flowInfo; >- >- int nullStatus = this.initialization.nullStatus(flowInfo); >- flowInfo = >- this.initialization >- .analyseCode(currentScope, flowContext, flowInfo) >- .unconditionalInits(); >- >- flowInfo.markAsDefinitelyAssigned(binding); >+ // record variable initialization if any >+ if (flowInfo.isReachable()) { >+ bits |= IsLocalDeclarationReachable; // only set if actually reached >+ } >+ if (this.initialization == null) { >+ return flowInfo; >+ } >+ int nullStatus = this.initialization.nullStatus(flowInfo); >+ flowInfo = >+ this.initialization >+ .analyseCode(currentScope, flowContext, flowInfo) >+ .unconditionalInits(); >+ flowInfo.markAsDefinitelyAssigned(binding); >+ if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) { > switch(nullStatus) { > case FlowInfo.NULL : > flowInfo.markAsDefinitelyNull(this.binding); >@@ -58,9 +55,12 @@ > case FlowInfo.NON_NULL : > flowInfo.markAsDefinitelyNonNull(this.binding); > break; >+ default: >+ flowInfo.markAsDefinitelyUnknown(this.binding); > } >- return flowInfo; > } >+ return flowInfo; >+} > > public void checkModifiers() { > >Index: compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java,v >retrieving revision 1.103 >diff -u -r1.103 MessageSend.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 18 Nov 2005 20:26:12 -0000 1.103 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 28 Nov 2005 12:08:05 -0000 >@@ -42,7 +42,9 @@ > > boolean nonStatic = !binding.isStatic(); > flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits(); >- if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >+ if (nonStatic) { >+ receiver.checkNPE(currentScope, flowContext, flowInfo, true); >+ } > > if (arguments != null) { > int length = arguments.length; >@@ -53,7 +55,10 @@ > ReferenceBinding[] thrownExceptions; > if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) { > // must verify that exceptions potentially thrown by this expression are caught in the method >- flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope); >+ flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope); >+ // TODO (maxime) the copy above is needed because of a side effect into >+ // checkExceptionHandlers; consider protecting there instead of here; >+ // NullReferenceTest#test0510 > } > manageSyntheticAccessIfNecessary(currentScope, flowInfo); > return flowInfo; >Index: compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java,v >retrieving revision 1.30 >diff -u -r1.30 OR_OR_Expression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java 18 Nov 2005 16:46:21 -0000 1.30 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java 28 Nov 2005 12:08:05 -0000 >@@ -50,7 +50,7 @@ > > // need to be careful of scenario: > // (x || y) || !z, if passing the left info to the right, it would be swapped by the ! >- FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalInits().copy(); >+ FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy(); > rightInitStateIndex = > currentScope.methodScope().recordInitializationStates(rightInfo); > >@@ -59,14 +59,11 @@ > rightInfo.setReachMode(FlowInfo.UNREACHABLE); > } > rightInfo = right.analyseCode(currentScope, flowContext, rightInfo); >- FlowInfo falseMergedInfo = rightInfo.initsWhenFalse().copy(); >- rightInfo.setReachMode(previousMode); // reset after falseMergedInfo got extracted >- > FlowInfo mergedInfo = FlowInfo.conditional( > // merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // b may not have been initialized >- leftInfo.initsWhenTrue().copy().unconditionalInits().mergedWith( >- rightInfo.initsWhenTrue().copy().unconditionalInits()), >- falseMergedInfo); >+ leftInfo.initsWhenTrue().unconditionalInits().mergedWith( >+ rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()), >+ rightInfo.initsWhenFalse()); > mergedInitStateIndex = > currentScope.methodScope().recordInitializationStates(mergedInfo); > return mergedInfo; >Index: compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java,v >retrieving revision 1.98 >diff -u -r1.98 QualifiedNameReference.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 18 Nov 2005 16:46:21 -0000 1.98 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 28 Nov 2005 12:08:06 -0000 >@@ -98,7 +98,7 @@ > } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { > localBinding.useFlag = LocalVariableBinding.FAKE_USED; > } >- this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >+ checkNPE(currentScope, flowContext, flowInfo, true); > } > > if (needValue) { >@@ -257,7 +257,7 @@ > } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { > localBinding.useFlag = LocalVariableBinding.FAKE_USED; > } >- this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL); >+ checkNPE(currentScope, flowContext, flowInfo, true); > } > if (needValue) { > manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); >@@ -304,7 +304,23 @@ > bits |= Binding.FIELD; > return getOtherFieldBindings(scope); > } >- >+ >+public void checkNPE(BlockScope scope, FlowContext flowContext, >+ FlowInfo flowInfo, boolean checkString) { >+ // cannot override localVariableBinding because this would project o.m onto o when >+ // analysing assignments >+ if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) { >+ LocalVariableBinding local = (LocalVariableBinding) this.binding; >+ if (local != null && >+ (local.type.tagBits & TagBits.IsBaseType) == 0 && >+ (checkString || local.type.id != T_JavaLangString)) { >+ flowContext.recordUsingNullReference(scope, local, this, >+ FlowContext.MAY_NULL, flowInfo); >+ flowInfo.markAsComparedEqualToNonNull(local); >+ // from thereon it is set >+ } >+ } >+} > /** > * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) > */ >@@ -773,6 +789,7 @@ > ? type.capture(scope, this.sourceEnd) > : type; > } >+ > public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { > if (!flowInfo.isReachable()) return; > //If inlinable field, forget the access emulation, the code gen will directly target it >@@ -849,6 +866,10 @@ > } > } > >+public int nullStatus(FlowInfo flowInfo) { >+ return FlowInfo.UNKNOWN; >+} >+ > public Constant optimizedBooleanConstant() { > > switch (this.resolvedType.id) { >Index: compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java,v >retrieving revision 1.25 >diff -u -r1.25 Reference.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 18 Nov 2005 16:46:21 -0000 1.25 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 28 Nov 2005 12:08:06 -0000 >@@ -68,20 +68,4 @@ > public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired); > > public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired); >- >-public int nullStatus(FlowInfo flowInfo) { >- >- if (this.constant != null && this.constant != Constant.NotAConstant) >- return FlowInfo.NON_NULL; // constant expression cannot be null >- >- LocalVariableBinding local = localVariableBinding(); >- if (local != null) { >- if (flowInfo.isDefinitelyNull(local)) >- return FlowInfo.NULL; >- if (flowInfo.isDefinitelyNonNull(local)) >- return FlowInfo.NON_NULL; >- } >- return FlowInfo.UNKNOWN; >-} >- > } >Index: compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java,v >retrieving revision 1.80 >diff -u -r1.80 SingleNameReference.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 18 Nov 2005 16:46:21 -0000 1.80 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 28 Nov 2005 12:08:06 -0000 >@@ -676,6 +676,27 @@ > } > } > } >+ >+public int nullStatus(FlowInfo flowInfo) { >+ if (this.constant != null && this.constant != Constant.NotAConstant) { >+ return FlowInfo.NON_NULL; // constant expression cannot be null >+ } >+ switch (bits & RestrictiveFlagMASK) { >+ case Binding.FIELD : // reading a field >+ return FlowInfo.UNKNOWN; >+ case Binding.LOCAL : // reading a local variable >+ LocalVariableBinding local = (LocalVariableBinding) this.binding; >+ if (local != null) { >+ if (flowInfo.isDefinitelyNull(local)) >+ return FlowInfo.NULL; >+ if (flowInfo.isDefinitelyNonNull(local)) >+ return FlowInfo.NON_NULL; >+ return FlowInfo.UNKNOWN; >+ } >+ } >+ return FlowInfo.NON_NULL; >+} >+ > public StringBuffer printExpression(int indent, StringBuffer output){ > > return output.append(token); >Index: compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java,v >retrieving revision 1.59 >diff -u -r1.59 SwitchStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 20 Oct 2005 13:26:45 -0000 1.59 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 28 Nov 2005 12:08:06 -0000 >@@ -62,11 +62,11 @@ > if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case > this.scope.enclosingCase = cases[caseIndex]; // record entering in a switch case block > caseIndex++; >- caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); >+ caseInits = caseInits.mergedWith(flowInfo.unconditionalInits()); > didAlreadyComplain = false; // reset complaint > } else if (statement == defaultCase) { // statement is the default case > this.scope.enclosingCase = defaultCase; // record entering in a switch case block >- caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); >+ caseInits = caseInits.mergedWith(flowInfo.unconditionalInits()); > didAlreadyComplain = false; // reset complaint > } > if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) { >Index: compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java,v >retrieving revision 1.85 >diff -u -r1.85 TryStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 18 Nov 2005 16:46:21 -0000 1.85 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 28 Nov 2005 12:08:06 -0000 >@@ -85,7 +85,7 @@ > .analyseCode( > currentScope, > finallyContext = new FinallyFlowContext(flowContext, finallyBlock), >- flowInfo.copy().unconditionalInits().discardNullRelatedInitializations()) >+ flowInfo.nullInfoLessUnconditionalCopy()) > .unconditionalInits(); > if (subInfo == FlowInfo.DEAD_END) { > isSubRoutineEscaping = true; >@@ -121,13 +121,15 @@ > 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() >- .unconditionalInits() >+ flowInfo.unconditionalCopy(). >+ addPotentialInitializationsFrom( >+ handlingContext.initsOnException( >+ caughtExceptionTypes[i])) > .addPotentialInitializationsFrom( >- handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits()) >- .addPotentialInitializationsFrom(tryInfo.unconditionalInits()) >- .addPotentialInitializationsFrom(handlingContext.initsOnReturn); >+ tryInfo.unconditionalInits()) >+ .addPotentialInitializationsFrom( >+ handlingContext.initsOnReturn); >+ // WORK this one is wrong -- too permissive for test case NullReferenceTest#test0560 > > // catch var is always set > LocalVariableBinding catchArg = catchArguments[i].binding; >@@ -162,10 +164,11 @@ > > // 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 >- finallyContext.complainOnDeferredChecks( >+ finallyContext.complainOnDeferredChecks( //$NON-NULL-1$ null with subRoutineStartLabel, which returns > tryInfo.isReachable() >- ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn)) >- : insideSubContext.initsOnReturn, >+ ? (tryInfo.addPotentialInitializationsFrom( >+ insideSubContext.initsOnReturn)) //$NON-NULL-1$ null with subRoutineStartLabel, which returns >+ : insideSubContext.initsOnReturn, //$NON-NULL-1$ null with subRoutineStartLabel, which returns > currentScope); > if (subInfo == FlowInfo.DEAD_END) { > mergedInitStateIndex = >Index: compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java,v >retrieving revision 1.109 >diff -u -r1.109 TypeDeclaration.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 20 Oct 2005 13:26:45 -0000 1.109 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 28 Nov 2005 12:08:07 -0000 >@@ -634,7 +634,7 @@ > * Common flow analysis for all types > * > */ >- public void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { >+ private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { > > if ((this.binding.isPrivate()/* || (this.binding.tagBits & (TagBits.IsAnonymousType|TagBits.IsLocalType)) == TagBits.IsLocalType*/) && !this.binding.isUsed()) { > if (!scope.referenceCompilationUnit().compilationResult.hasSyntaxError) { >@@ -644,8 +644,8 @@ > > InitializationFlowContext initializerContext = new InitializationFlowContext(null, this, initializerScope); > InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, staticInitializerScope); >- FlowInfo nonStaticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); >- FlowInfo staticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); >+ FlowInfo nonStaticFieldInfo = flowInfo.unconditionalFieldLessCopy(); >+ FlowInfo staticFieldInfo = flowInfo.unconditionalFieldLessCopy(); > if (fields != null) { > for (int i = 0, count = fields.length; i < count; i++) { > FieldDeclaration field = fields[i]; >@@ -699,7 +699,7 @@ > } > } > if (methods != null) { >- UnconditionalFlowInfo outerInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations(); >+ UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy(); > FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo); > for (int i = 0, count = methods.length; i < count; i++) { > AbstractMethodDeclaration method = methods[i]; >Index: compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java,v >retrieving revision 1.38 >diff -u -r1.38 UnaryExpression.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 18 Nov 2005 16:46:22 -0000 1.38 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 28 Nov 2005 12:08:07 -0000 >@@ -27,19 +27,20 @@ > this.bits |= operator << OperatorSHIFT; // encode operator > } > >- public FlowInfo analyseCode( >+public FlowInfo analyseCode( > BlockScope currentScope, > FlowContext flowContext, > FlowInfo flowInfo) { >- >- if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) { >- return this.expression >- .analyseCode(currentScope, flowContext, flowInfo) >- .asNegatedCondition(); >- } else { >- return this.expression.analyseCode(currentScope, flowContext, flowInfo); >- } >+ this.expression.checkNPE(currentScope, flowContext, flowInfo, true); >+ if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) { >+ return this.expression. >+ analyseCode(currentScope, flowContext, flowInfo). >+ asNegatedCondition(); >+ } else { >+ return this.expression. >+ analyseCode(currentScope, flowContext, flowInfo); > } >+} > > public Constant optimizedBooleanConstant() { > >Index: compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java,v >retrieving revision 1.51 >diff -u -r1.51 WhileStatement.java >--- compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 18 Nov 2005 16:46:21 -0000 1.51 >+++ compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 28 Nov 2005 12:08:07 -0000 >@@ -55,11 +55,15 @@ > preCondInitStateIndex = > currentScope.methodScope().recordInitializationStates(flowInfo); > LoopingFlowContext condLoopContext; >- FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations(); >+ FlowInfo condInfo = flowInfo.nullInfoLessUnconditionalCopy(); >+ // we need to collect the contribution to nulls of the coming paths through the >+ // loop, be they falling through normally or branched to break, continue labels >+ // or catch blocks > condInfo = this.condition.analyseCode( > currentScope, > (condLoopContext = >- new LoopingFlowContext(flowContext, this, null, null, currentScope)), >+ new LoopingFlowContext(flowContext, flowInfo, this, null, >+ null, currentScope)), > condInfo); > > LoopingFlowContext loopingContext; >@@ -67,17 +71,22 @@ > FlowInfo exitBranch; > if (action == null > || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) { >- condLoopContext.complainOnDeferredChecks(currentScope, condInfo); >+ condLoopContext.complainOnDeferredFinalChecks(currentScope, >+ condInfo); >+ condLoopContext.complainOnDeferredNullChecks(currentScope, >+ condInfo.unconditionalInits()); > if (isConditionTrue) { > return FlowInfo.DEAD_END; > } else { >- FlowInfo mergedInfo = condInfo.initsWhenFalse().unconditionalInits(); >+ FlowInfo mergedInfo = condInfo.initsWhenFalse(); > if (isConditionOptimizedTrue){ > mergedInfo.setReachMode(FlowInfo.UNREACHABLE); > } > mergedInitStateIndex = > currentScope.methodScope().recordInitializationStates(mergedInfo); >- return mergedInfo; >+ return flowInfo.unconditionalInits(). >+ addPotentialNullInfoFrom( >+ condInfo.initsWhenFalse().unconditionalInits()); > } > } else { > // in case the condition was inlined to false, record the fact that there is no way to reach any >@@ -85,6 +94,7 @@ > loopingContext = > new LoopingFlowContext( > flowContext, >+ flowInfo, > this, > breakLabel, > continueLabel, >@@ -108,15 +118,26 @@ > } > > // code generation can be optimized when no need to continue in the loop >- exitBranch = condInfo.initsWhenFalse(); >- exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis >+ exitBranch = flowInfo.copy(); >+ // need to start over from flowInfo so as to get null inits >+ > if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) { > continueLabel = null; >+ exitBranch.addInitializationsFrom(condInfo.initsWhenFalse()); > } else { >- condLoopContext.complainOnDeferredChecks(currentScope, condInfo); >+ condLoopContext.complainOnDeferredFinalChecks(currentScope, >+ condInfo); > actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits()); >- loopingContext.complainOnDeferredChecks(currentScope, actionInfo); >- exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits()); >+ condLoopContext.complainOnDeferredNullChecks(currentScope, >+ actionInfo); >+ loopingContext.complainOnDeferredFinalChecks(currentScope, >+ actionInfo); >+ loopingContext.complainOnDeferredNullChecks(currentScope, >+ actionInfo); >+ exitBranch. >+ addPotentialInitializationsFrom( >+ actionInfo.unconditionalInits()). >+ addInitializationsFrom(condInfo.initsWhenFalse()); > } > } > >Index: compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java,v >retrieving revision 1.23 >diff -u -r1.23 ConditionalFlowInfo.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 23 Feb 2005 02:47:29 -0000 1.23 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java 28 Nov 2005 12:08:07 -0000 >@@ -83,52 +83,20 @@ > && initsWhenFalse.isDefinitelyAssigned(local); > } > >- /** >- * Check status of definite non-null assignment for a field. >- */ >- public boolean isDefinitelyNonNull(FieldBinding field) { >- >- return initsWhenTrue.isDefinitelyNonNull(field) >- && initsWhenFalse.isDefinitelyNonNull(field); >- } >- >- /** >- * Check status of definite non-null assignment for a local variable. >- */ >- public boolean isDefinitelyNonNull(LocalVariableBinding local) { >- >- return initsWhenTrue.isDefinitelyNonNull(local) >- && initsWhenFalse.isDefinitelyNonNull(local); >- } >+public boolean isDefinitelyNonNull(LocalVariableBinding local) { >+ return initsWhenTrue.isDefinitelyNonNull(local) >+ && initsWhenFalse.isDefinitelyNonNull(local); >+} > >- /** >- * Check status of definite null assignment for a field. >- */ >- public boolean isDefinitelyNull(FieldBinding field) { >- >- return initsWhenTrue.isDefinitelyNull(field) >- && initsWhenFalse.isDefinitelyNull(field); >- } >- >- /** >- * Check status of definite null assignment for a local variable. >- */ >- public boolean isDefinitelyNull(LocalVariableBinding local) { >- >- return initsWhenTrue.isDefinitelyNull(local) >- && initsWhenFalse.isDefinitelyNull(local); >- } >+public boolean isDefinitelyNull(LocalVariableBinding local) { >+ return initsWhenTrue.isDefinitelyNull(local) >+ && initsWhenFalse.isDefinitelyNull(local); >+} > >- public int reachMode(){ >- return unconditionalInits().reachMode(); >- } >- >- public boolean isReachable(){ >- >- return unconditionalInits().isReachable(); >- //should maybe directly be: false >- } >- >+public boolean isDefinitelyUnknown(LocalVariableBinding local) { >+ return initsWhenTrue.isDefinitelyUnknown(local) >+ && initsWhenFalse.isDefinitelyUnknown(local); >+} > /** > * Check status of potential assignment for a field. > */ >@@ -147,6 +115,36 @@ > || initsWhenFalse.isPotentiallyAssigned(local); > } > >+public boolean isPotentiallyNull(LocalVariableBinding local) { >+ return initsWhenTrue.isPotentiallyNull(local) >+ || initsWhenFalse.isPotentiallyNull(local); >+} >+ >+public boolean isPotentiallyUnknown(LocalVariableBinding local) { >+ return initsWhenTrue.isPotentiallyUnknown(local) >+ || initsWhenFalse.isPotentiallyUnknown(local); >+} >+ >+public boolean isProtectedNonNull(LocalVariableBinding local) { >+ return initsWhenTrue.isProtectedNonNull(local) >+ && initsWhenFalse.isProtectedNonNull(local); >+} >+ >+public boolean isProtectedNull(LocalVariableBinding local) { >+ return initsWhenTrue.isProtectedNull(local) >+ && initsWhenFalse.isProtectedNull(local); >+} >+ >+public void markAsComparedEqualToNonNull(LocalVariableBinding local) { >+ initsWhenTrue.markAsComparedEqualToNonNull(local); >+ initsWhenFalse.markAsComparedEqualToNonNull(local); >+} >+ >+public void markAsComparedEqualToNull(LocalVariableBinding local) { >+ initsWhenTrue.markAsComparedEqualToNull(local); >+ initsWhenFalse.markAsComparedEqualToNull(local); >+} >+ > /** > * Record a field got definitely assigned. > */ >@@ -201,50 +199,60 @@ > initsWhenFalse.markAsDefinitelyNull(local); > } > >- /** >- * Clear the initialization info for a field >- */ >- public void markAsDefinitelyNotAssigned(FieldBinding field) { >- >- initsWhenTrue.markAsDefinitelyNotAssigned(field); >- initsWhenFalse.markAsDefinitelyNotAssigned(field); >- } >- >- /** >- * Clear the initialization info for a local variable >- */ >- public void markAsDefinitelyNotAssigned(LocalVariableBinding local) { >- >- initsWhenTrue.markAsDefinitelyNotAssigned(local); >- initsWhenFalse.markAsDefinitelyNotAssigned(local); >- } >- >- public FlowInfo setReachMode(int reachMode) { >- >- initsWhenTrue.setReachMode(reachMode); >- initsWhenFalse.setReachMode(reachMode); >- return this; >- } >- >- /** >- * Converts conditional receiver into inconditional one, updated in the following way: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { >- >- return unconditionalInits().mergedWith(otherInits); >- } >+public void markAsDefinitelyUnknown(LocalVariableBinding local) { >+ initsWhenTrue.markAsDefinitelyUnknown(local); >+ initsWhenFalse.markAsDefinitelyUnknown(local); >+} >+ >+public FlowInfo setReachMode(int reachMode) { >+ if (reachMode == REACHABLE) { >+ this.tagBits &= ~UNREACHABLE; >+ } >+ else { >+ this.tagBits |= UNREACHABLE; >+ } >+ initsWhenTrue.setReachMode(reachMode); >+ initsWhenFalse.setReachMode(reachMode); >+ return this; >+} > >+public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { >+ return unconditionalInits().mergedWith(otherInits); >+} >+ >+public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { >+ return unconditionalInitsWithoutSideEffect(). >+ nullInfoLessUnconditionalCopy(); >+} > public String toString() { > > return "FlowInfo<true: " + initsWhenTrue.toString() + ", false: " + initsWhenFalse.toString() + ">"; //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$ > } >- >- public UnconditionalFlowInfo unconditionalInits() { >- >- return initsWhenTrue.unconditionalInits().copy() >- .mergedWith(initsWhenFalse.unconditionalInits()); >- } >+ >+public FlowInfo safeInitsWhenTrue() { >+ return initsWhenTrue; >+} >+ >+public UnconditionalFlowInfo unconditionalCopy() { >+ return initsWhenTrue.unconditionalCopy(). >+ mergedWith(initsWhenFalse.unconditionalInits()); >+} >+ >+public UnconditionalFlowInfo unconditionalFieldLessCopy() { >+ return initsWhenTrue.unconditionalFieldLessCopy(). >+ mergedWith(initsWhenFalse.unconditionalFieldLessCopy()); >+ // should never happen, hence suboptimal does not hurt >+} >+ >+public UnconditionalFlowInfo unconditionalInits() { >+ return initsWhenTrue.unconditionalInits(). >+ mergedWith(initsWhenFalse.unconditionalInits()); >+} >+ >+public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { >+ // cannot do better here than unconditionalCopy - but still a different >+ // operation for UnconditionalFlowInfo >+ return initsWhenTrue.unconditionalCopy(). >+ mergedWith(initsWhenFalse.unconditionalInits()); >+} > } >Index: compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java,v >retrieving revision 1.33 >diff -u -r1.33 ExceptionHandlingFlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 20 Oct 2005 13:26:46 -0000 1.33 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -62,7 +62,7 @@ > int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize); > if (handledExceptions[i].isUncheckedException(true)) { > isReached[cacheIndex] |= bitMask; >- this.initsOnExceptions[i] = flowInfo.copy().unconditionalInits(); >+ this.initsOnExceptions[i] = flowInfo.unconditionalCopy(); > } else { > this.initsOnExceptions[i] = FlowInfo.DEAD_END; > } >@@ -168,20 +168,22 @@ > this.isReached[cacheIndex] |= bitMask; > > initsOnExceptions[index] = >- initsOnExceptions[index] == FlowInfo.DEAD_END >- ? flowInfo.copy().unconditionalInits() >- : initsOnExceptions[index].mergedWith(flowInfo.copy().unconditionalInits()); >+ initsOnExceptions[index].isReachable() ? >+ initsOnExceptions[index].mergedWith(flowInfo): >+ flowInfo.unconditionalCopy(); > } > >- public void recordReturnFrom(FlowInfo flowInfo) { >- >- if (!flowInfo.isReachable()) return; >- if (initsOnReturn == FlowInfo.DEAD_END) { >- initsOnReturn = flowInfo.copy().unconditionalInits(); >- } else { >- initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits()); >- } >+public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { >+ if (!flowInfo.isReachable()) { >+ return; >+ } >+ if (initsOnReturn.isReachable()) { >+ initsOnReturn = initsOnReturn.mergedWith(flowInfo); >+ } >+ else { >+ initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy(); > } >+} > > /* > * Compute a merged list of unhandled exception types (keeping only the most generic ones). >Index: compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java,v >retrieving revision 1.17 >diff -u -r1.17 FinallyFlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 23 Feb 2005 02:47:29 -0000 1.17 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -16,6 +16,7 @@ > import org.eclipse.jdt.internal.compiler.lookup.BlockScope; > import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; > import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; >+import org.eclipse.jdt.internal.compiler.lookup.Scope; > import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; > > /** >@@ -28,77 +29,124 @@ > VariableBinding[] finalVariables; > int assignCount; > >+ LocalVariableBinding[] nullLocals; > Expression[] nullReferences; >- int[] nullStatus; >+ int[] nullCheckTypes; > int nullCount; > > public FinallyFlowContext(FlowContext parent, ASTNode associatedNode) { > super(parent, associatedNode); > } > >- /** >- * Given some contextual initialization info (derived from a try block or a catch block), this >- * code will check that the subroutine context does not also initialize a final variable potentially set >- * redundantly. >- */ >- public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { >+/** >+ * Given some contextual initialization info (derived from a try block or a catch block), this >+ * code will check that the subroutine context does not also initialize a final variable potentially set >+ * redundantly. >+ */ >+public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { >+ >+ // check redundant final assignments >+ for (int i = 0; i < this.assignCount; i++) { >+ VariableBinding variable = this.finalVariables[i]; >+ if (variable == null) continue; > >- // check redundant final assignments >- for (int i = 0; i < assignCount; i++) { >- VariableBinding variable = finalVariables[i]; >- if (variable == null) continue; >- >- boolean complained = false; // remember if have complained on this final assignment >- if (variable instanceof FieldBinding) { >- // final field >- if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) { >- complained = true; >- scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, finalAssignments[i]); >- } >- } else { >- // final local variable >- if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { >- complained = true; >- scope.problemReporter().duplicateInitializationOfFinalLocal( >- (LocalVariableBinding) variable, >- finalAssignments[i]); >- } >- } >- // any reference reported at this level is removed from the parent context >- // where it could also be reported again >- if (complained) { >- FlowContext currentContext = parent; >- while (currentContext != null) { >- //if (currentContext.isSubRoutine()) { >- currentContext.removeFinalAssignmentIfAny(finalAssignments[i]); >- //} >- currentContext = currentContext.parent; >- } >+ boolean complained = false; // remember if have complained on this final assignment >+ if (variable instanceof FieldBinding) { >+ // final field >+ if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) { >+ complained = true; >+ scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, finalAssignments[i]); >+ } >+ } else { >+ // final local variable >+ if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { >+ complained = true; >+ scope.problemReporter().duplicateInitializationOfFinalLocal( >+ (LocalVariableBinding) variable, >+ this.finalAssignments[i]); > } > } >- >- // check inconsistent null checks >- for (int i = 0; i < nullCount; i++) { >- Expression expression = nullReferences[i]; >- if (expression == null) continue; >+ // any reference reported at this level is removed from the parent context >+ // where it could also be reported again >+ if (complained) { >+ FlowContext currentContext = this.parent; >+ while (currentContext != null) { >+ //if (currentContext.isSubRoutine()) { >+ currentContext.removeFinalAssignmentIfAny(this.finalAssignments[i]); >+ //} >+ currentContext = currentContext.parent; >+ } >+ } >+ } >+ >+ // check inconsistent null checks >+ if (this.deferNullDiagnostic) { // within an enclosing loop, be conservative >+ for (int i = 0; i < this.nullCount; i++) { >+ Expression expression = this.nullReferences[i]; >+ LocalVariableBinding local = this.nullLocals[i]; >+ switch (this.nullCheckTypes[i]) { >+ case CAN_ONLY_NULL_NON_NULL : >+ case CAN_ONLY_NULL: >+ if (flowInfo.isProtectedNonNull(local)) { >+ if (nullCheckTypes[i] == CAN_ONLY_NULL_NON_NULL) { >+ scope.problemReporter().localVariableCannotBeNull(local, expression); >+ } >+ return; >+ } >+ if (flowInfo.isProtectedNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ return; >+ } >+ break; >+ case MAY_NULL : >+ if (flowInfo.isProtectedNonNull(local)) { >+ return; >+ } >+ if (flowInfo.isProtectedNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ return; >+ } >+ break; >+ default: >+ // never happens >+ } >+ this.parent.recordUsingNullReference(scope, local, expression, >+ this.nullCheckTypes[i], flowInfo); >+ } >+ } >+ else { // no enclosing loop, be as precise as possible right now >+ for (int i = 0; i < this.nullCount; i++) { >+ Expression expression = this.nullReferences[i]; > // final local variable >- LocalVariableBinding local = expression.localVariableBinding(); >- switch (nullStatus[i]) { >- case FlowInfo.NULL : >+ LocalVariableBinding local = this.nullLocals[i]; >+ switch (this.nullCheckTypes[i]) { >+ case CAN_ONLY_NULL_NON_NULL : >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ scope.problemReporter().localVariableCannotBeNull(local, expression); >+ return; >+ } >+ case CAN_ONLY_NULL: > if (flowInfo.isDefinitelyNull(local)) { >- nullReferences[i] = null; >- this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ return; > } > break; >- case FlowInfo.NON_NULL : >- if (flowInfo.isDefinitelyNonNull(local)) { >- nullReferences[i] = null; >- this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); >+ case MAY_NULL : >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ return; >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ scope.problemReporter().localVariableMayBeNull(local, expression); >+ return; > } > break; >+ default: >+ // should not happen > } > } > } >+} > > public String individualToString() { > >@@ -138,6 +186,73 @@ > return true; > } > >+ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, >+ Expression reference, int checkType, FlowInfo flowInfo) { >+ if (!flowInfo.isReachable()) { >+ return; >+ } >+ if (deferNullDiagnostic) { // within an enclosing loop, be conservative >+ switch (checkType) { >+ case CAN_ONLY_NULL_NON_NULL : >+ case CAN_ONLY_NULL: >+ if (flowInfo.isProtectedNonNull(local)) { >+ if (checkType == CAN_ONLY_NULL_NON_NULL) { >+ scope.problemReporter().localVariableCannotBeNull(local, reference); >+ } >+ return; >+ } >+ if (flowInfo.isProtectedNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ break; >+ case MAY_NULL : >+ if (flowInfo.isProtectedNonNull(local)) { >+ return; >+ } >+ if (flowInfo.isProtectedNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ break; >+ default: >+ // never happens >+ } >+ } >+ else { // no enclosing loop, be as precise as possible right now >+ switch (checkType) { >+ case CAN_ONLY_NULL_NON_NULL : >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ scope.problemReporter().localVariableCannotBeNull(local, reference); >+ return; >+ } >+ case CAN_ONLY_NULL: >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ break; >+ case MAY_NULL : >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ scope.problemReporter().localVariableMayBeNull(local, reference); >+ return; >+ } >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ return; // shortcut: cannot be null >+ } >+ break; >+ default: >+ // never happens >+ } >+ } >+ recordNullReference(local, reference, checkType); >+ // prepare to re-check with try/catch flow info >+ } >+ > void removeFinalAssignmentIfAny(Reference reference) { > for (int i = 0; i < assignCount; i++) { > if (finalAssignments[i] == reference) { >@@ -148,18 +263,27 @@ > } > } > >- protected boolean recordNullReference(Expression expression, int status) { >- if (nullCount == 0) { >- nullReferences = new Expression[5]; >- nullStatus = new int[5]; >- } else { >- if (nullCount == nullReferences.length) { >- System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount); >- System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount); >- } >- } >- nullReferences[nullCount] = expression; >- nullStatus[nullCount++] = status; >- return true; >+protected void recordNullReference(LocalVariableBinding local, >+ Expression expression, int status) { >+ if (this.nullCount == 0) { >+ this.nullLocals = new LocalVariableBinding[5]; >+ this.nullReferences = new Expression[5]; >+ this.nullCheckTypes = new int[5]; >+ } >+ else if (this.nullCount == this.nullLocals.length) { >+ int newLength = this.nullCount * 2; >+ System.arraycopy(this.nullLocals, 0, >+ this.nullLocals = new LocalVariableBinding[newLength], 0, >+ this.nullCount); >+ System.arraycopy(this.nullReferences, 0, >+ this.nullReferences = new Expression[newLength], 0, >+ this.nullCount); >+ System.arraycopy(this.nullCheckTypes, 0, >+ this.nullCheckTypes = new int[newLength], 0, >+ this.nullCount); > } >+ this.nullLocals[this.nullCount] = local; >+ this.nullReferences[this.nullCount] = expression; >+ this.nullCheckTypes[this.nullCount++] = status; >+} > } >Index: compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java,v >retrieving revision 1.45 >diff -u -r1.45 FlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 14 Oct 2005 22:43:00 -0000 1.45 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -35,12 +35,15 @@ > > public ASTNode associatedNode; > public FlowContext parent; >- >+ boolean deferNullDiagnostic, preemptNullDiagnostic; >+ // preempt marks looping contexts > public final static FlowContext NotContinuableContext = new FlowContext(null, null); > > public FlowContext(FlowContext parent, ASTNode associatedNode) { > this.parent = parent; > this.associatedNode = associatedNode; >+ deferNullDiagnostic = parent != null && >+ (parent.deferNullDiagnostic || parent.preemptNullDiagnostic); > } > > public Label breakLabel() { >@@ -163,7 +166,7 @@ > traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); > if (traversedContext.associatedNode instanceof TryStatement){ > TryStatement tryStatement = (TryStatement) traversedContext.associatedNode; >- flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits); >+ flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); > } > traversedContext = traversedContext.parent; > } >@@ -256,7 +259,7 @@ > traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); > if (traversedContext.associatedNode instanceof TryStatement){ > TryStatement tryStatement = (TryStatement) traversedContext.associatedNode; >- flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits); >+ flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); > } > traversedContext = traversedContext.parent; > } >@@ -403,7 +406,7 @@ > // default implementation: do nothing > } > >-public void recordContinueFrom(FlowInfo flowInfo) { >+public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { > // default implementation: do nothing > } > >@@ -411,11 +414,21 @@ > return true; // keep going > } > >-protected boolean recordNullReference(Expression expression, int status) { >- return false; // keep going >+/** >+ * Record a null reference for use by deferred checks. Only looping or >+ * finally contexts really record that information. >+ * @param local the local variable involved in the check >+ * @param expression the expression within which local lays >+ * @param status the status against which the check must be performed; one of >+ * {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL >+ * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL} >+ */ >+protected void recordNullReference(LocalVariableBinding local, >+ Expression expression, int status) { >+ // default implementation: do nothing > } > >-public void recordReturnFrom(FlowInfo flowInfo) { >+public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { > // default implementation: do nothing > } > >@@ -432,34 +445,72 @@ > } > } > >-public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int status, FlowInfo flowInfo) { >- if (!flowInfo.isReachable()) return; >- >- switch (status) { >- case FlowInfo.NULL : >+public static final int >+ CAN_ONLY_NULL_NON_NULL = 20, >+ // check against null and non null, with definite values -- comparisons >+ CAN_ONLY_NULL = 21, >+ // check against null, with definite values -- assignment to null >+ MAY_NULL = 22; >+ // check against null, with potential values -- NPE guard >+ >+/** >+ * Record a null reference for use by deferred checks. Only looping or >+ * finally contexts really record that information. The context may >+ * emit an error immediately depending on the status of local against >+ * flowInfo and its nature (only looping of finally contexts defer part >+ * of the checks; nonetheless, contexts that are nested into a looping or a >+ * finally context get affected and delegate some checks to their enclosing >+ * context). >+ * @param scope the scope into which the check is performed >+ * @param local the local variable involved in the check >+ * @param reference the expression within which local lays >+ * @param checkType the status against which the check must be performed; one >+ * of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL >+ * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL} >+ * @param flowInfo the flow info at the check point; deferring contexts will >+ * perform supplementary checks against flow info instances that cannot >+ * be known at the time of calling this method (they are influenced by >+ * code that follows the current point) >+ */ >+public void recordUsingNullReference(Scope scope, LocalVariableBinding local, >+ Expression reference, int checkType, FlowInfo flowInfo) { >+ if (!flowInfo.isReachable() || flowInfo.isDefinitelyUnknown(local)) { >+ return; >+ } >+ switch (checkType) { >+ case CAN_ONLY_NULL_NON_NULL : >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ scope.problemReporter().localVariableCannotBeNull(local, reference); >+ return; >+ } >+ else if (flowInfo.isPotentiallyUnknown(local)) { >+ return; >+ } >+ case CAN_ONLY_NULL: > if (flowInfo.isDefinitelyNull(local)) { > scope.problemReporter().localVariableCanOnlyBeNull(local, reference); > return; >- } else if (flowInfo.isDefinitelyNonNull(local)) { >- scope.problemReporter().localVariableCannotBeNull(local, reference); >+ } >+ else if (flowInfo.isPotentiallyUnknown(local)) { > return; > } > break; >- case FlowInfo.NON_NULL : >+ case MAY_NULL : > if (flowInfo.isDefinitelyNull(local)) { >- scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ scope.problemReporter().localVariableMayBeNull(local, reference); > return; > } > break; >+ default: >+ // never happens > } >- >- // for initialization inside looping statement that effectively loops >- FlowContext context = this; >- while (context != null) { >- if (context.recordNullReference(reference, status)) { >- return; // no need to keep going >- } >- context = context.parent; >+ if (parent != null) { >+ parent.recordUsingNullReference(scope, local, reference, checkType, >+ flowInfo); > } > } > >Index: compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java,v >retrieving revision 1.27 >diff -u -r1.27 FlowInfo.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 23 Feb 2005 02:47:29 -0000 1.27 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java 28 Nov 2005 12:08:07 -0000 >@@ -15,8 +15,10 @@ > > public abstract class FlowInfo { > >+ public int tagBits; // REACHABLE by default > public final static int REACHABLE = 0; > public final static int UNREACHABLE = 1; >+ public final static int NULL_FLAG_MASK = 2; > > public final static int UNKNOWN = 0; > public final static int NULL = 1; >@@ -25,11 +27,30 @@ > public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization > static { > DEAD_END = new UnconditionalFlowInfo(); >- DEAD_END.reachMode = UNREACHABLE; >+ DEAD_END.tagBits = UNREACHABLE; > } >- abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits); > >- abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits); >+/** >+ * Add other inits to this flow info, then return this. The operation semantics >+ * are to match as closely as possible the application to this flow info of all >+ * the operations that resulted into otherInits. >+ * @param otherInits other inits to add to this >+ * @return this, modified according to otherInits information >+ */ >+abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits); >+ >+ >+/** >+ * Compose other inits over this flow info, then return this. The operation >+ * semantics are to wave into this flow info the consequences of a possible >+ * path into the operations that resulted into otherInits. The fact that this >+ * path may be left unexecuted under peculiar conditions results into less >+ * specific results than {@link #addInitializationsFrom(FlowInfo) >+ * addInitializationsFrom}. >+ * @param otherInits other inits to compose over this >+ * @return this, modified according to otherInits information >+ */ >+abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits); > > public FlowInfo asNegatedCondition() { > >@@ -42,6 +63,10 @@ > return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse); > } > >+/** >+ * Return a deep copy of the current instance. >+ * @return a deep copy of this flow info >+ */ > abstract public FlowInfo copy(); > > public static UnconditionalFlowInfo initial(int maxFieldCount) { >@@ -49,9 +74,27 @@ > info.maxFieldCount = maxFieldCount; > return info; > } >- >- abstract public FlowInfo initsWhenFalse(); >- >+ >+/** >+ * Return the flow info that would result from the path associated to the >+ * value false for the condition expression that generated this flow info. >+ * May be this flow info if it is not an instance of {@link >+ * ConditionalFlowInfo}. May have a side effect on subparts of this flow >+ * info (subtrees get merged). >+ * @return the flow info associated to the false branch of the condition >+ * that generated this flow info >+ */ >+abstract public FlowInfo initsWhenFalse(); >+ >+/** >+ * Return the flow info that would result from the path associated to the >+ * value true for the condition expression that generated this flow info. >+ * May be this flow info if it is not an instance of {@link >+ * ConditionalFlowInfo}. May have a side effect on subparts of this flow >+ * info (subtrees get merged). >+ * @return the flow info associated to the true branch of the condition >+ * that generated this flow info >+ */ > abstract public FlowInfo initsWhenTrue(); > > /** >@@ -64,25 +107,26 @@ > */ > public abstract boolean isDefinitelyAssigned(LocalVariableBinding local); > >- /** >- * Check status of definite null assignment for a field. >- */ >- abstract public boolean isDefinitelyNonNull(FieldBinding field); >- >- /** >- * Check status of definite null assignment for a local. >- */ >+/** >+ * Check status of definite non-null value for a given local variable. >+ * @param local the variable to ckeck >+ * @return true iff local is definitely non null for this flow info >+ */ > public abstract boolean isDefinitelyNonNull(LocalVariableBinding local); > >- /** >- * Check status of definite null assignment for a field. >- */ >- abstract public boolean isDefinitelyNull(FieldBinding field); >- >- /** >- * Check status of definite null assignment for a local. >- */ >- public abstract boolean isDefinitelyNull(LocalVariableBinding local); >+/** >+ * Check status of definite null value for a given local variable. >+ * @param local the variable to ckeck >+ * @return true iff local is definitely null for this flow info >+ */ >+public abstract boolean isDefinitelyNull(LocalVariableBinding local); >+ >+/** >+ * Check status of definite unknown value for a given local variable. >+ * @param local the variable to ckeck >+ * @return true iff local is definitely unknown for this flow info >+ */ >+public abstract boolean isDefinitelyUnknown(LocalVariableBinding local); > > /** > * Check status of potential assignment for a field. >@@ -95,7 +139,48 @@ > > abstract public boolean isPotentiallyAssigned(LocalVariableBinding field); > >- abstract public boolean isReachable(); >+/** >+ * Check status of potential null assignment for a local. >+ */ >+public abstract boolean isPotentiallyNull(LocalVariableBinding local); >+ >+/** >+ * Return true if the given local may have been assigned to an unknown value. >+ * @param local the local to check >+ * @return true if the given local may have been assigned to an unknown value >+ */ >+public abstract boolean isPotentiallyUnknown(LocalVariableBinding local); >+ >+/** >+ * Return true if the given local is protected by a test against a non null >+ * value. >+ * @param local the local to check >+ * @return true if the given local is protected by a test against a non null >+ */ >+public abstract boolean isProtectedNonNull(LocalVariableBinding local); >+ >+/** >+ * Return true if the given local is protected by a test against null. >+ * @param local the local to check >+ * @return true if the given local is protected by a test against null >+ */ >+public abstract boolean isProtectedNull(LocalVariableBinding local); >+ >+public boolean isReachable() { >+ return (this.tagBits & UNREACHABLE) == 0; >+} >+ >+/** >+ * Record that a local variable got checked to be non null. >+ * @param local the checked local variable >+ */ >+abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local); >+ >+/** >+ * Record that a local variable got checked to be null. >+ * @param local the checked local variable >+ */ >+abstract public void markAsComparedEqualToNull(LocalVariableBinding local); > > /** > * Record a field got definitely assigned. >@@ -118,7 +203,7 @@ > abstract public void markAsDefinitelyNull(LocalVariableBinding local); > > /** >- * Record a field got definitely assigned. >+ * Record a field got definitely assigned to null. > */ > abstract public void markAsDefinitelyNull(FieldBinding field); > >@@ -127,52 +212,99 @@ > */ > abstract public void markAsDefinitelyAssigned(LocalVariableBinding local); > >- /** >- * Clear the initialization info for a field >- */ >- abstract public void markAsDefinitelyNotAssigned(FieldBinding field); >- >- /** >- * Clear the initialization info for a local variable >- */ >- abstract public void markAsDefinitelyNotAssigned(LocalVariableBinding local); >- >- /** >- * Merge branches using optimized boolean conditions >- */ >- public static FlowInfo mergedOptimizedBranches(FlowInfo initsWhenTrue, boolean isOptimizedTrue, FlowInfo initsWhenFalse, boolean isOptimizedFalse, boolean allowFakeDeadBranch) { >- FlowInfo mergedInfo; >- if (isOptimizedTrue){ >- if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) { >- mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE); >- } else { >- mergedInfo = initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse); >- } >- >- } else if (isOptimizedFalse) { >- if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) { >- mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE); >- } else { >- mergedInfo = initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue); >- } >- >- } else { >- mergedInfo = initsWhenTrue.unconditionalInits().mergedWith(initsWhenFalse.unconditionalInits()); >+/** >+ * Record a local got definitely assigned to an unknown value. >+ */ >+abstract public void markAsDefinitelyUnknown(LocalVariableBinding local); >+ >+/** >+ * Merge branches using optimized boolean conditions >+ */ >+public static UnconditionalFlowInfo mergedOptimizedBranches( >+ FlowInfo initsWhenTrue, boolean isOptimizedTrue, >+ FlowInfo initsWhenFalse, boolean isOptimizedFalse, >+ boolean allowFakeDeadBranch) { >+ UnconditionalFlowInfo mergedInfo; >+ if (isOptimizedTrue){ >+ if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) { >+ mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE). >+ unconditionalInits(); >+ } >+ else { >+ mergedInfo = >+ initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse. >+ nullInfoLessUnconditionalCopy()). >+ unconditionalInits(); >+ } >+ } >+ else if (isOptimizedFalse) { >+ if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) { >+ mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE). >+ unconditionalInits(); >+ } >+ else { >+ mergedInfo = >+ initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue. >+ nullInfoLessUnconditionalCopy()). >+ unconditionalInits(); > } >- return mergedInfo; >+ } >+ else { >+ mergedInfo = initsWhenTrue. >+ mergedWith(initsWhenFalse.unconditionalInits()); > } >+ return mergedInfo; >+} > >- abstract public int reachMode(); >- >- abstract public FlowInfo setReachMode(int reachMode); >- >- /** >- * Returns the receiver updated in the following way: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- abstract public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits); >+/** >+ * Return REACHABLE if this flow info is reachable, UNREACHABLE >+ * else. >+ * @return REACHABLE if this flow info is reachable, UNREACHABLE >+ * else >+ */ >+public int reachMode() { >+ return this.tagBits & UNREACHABLE; >+} >+ >+/** >+ * Return a flow info that carries the same information as the result of >+ * {@link #initsWhenTrue() initsWhenTrue}, but warrantied to be different >+ * from this.<br> >+ * Caveat: side effects on the result may affect components of this. >+ * @return the result of initsWhenTrue or a copy of it >+ */ >+abstract public FlowInfo safeInitsWhenTrue(); >+ >+/** >+ * Set this flow info reach mode and return this. >+ * @param reachMode one of {@link #REACHABLE REACHABLE} or {@link #UNREACHABLE UNREACHABLE} >+ * @return this, with the reach mode set to reachMode >+ */ >+abstract public FlowInfo setReachMode(int reachMode); >+ >+/** >+ * Return the intersection of this and otherInits, that is >+ * one of:<ul> >+ * <li>the receiver updated in the following way:<ul> >+ * <li>intersection of definitely assigned variables, >+ * <li>union of potentially assigned variables, >+ * <li>similar operations for null,</ul> >+ * <li>or the receiver or otherInits if the other one is non >+ * reachable.</ul> >+ * otherInits is not affected, and is not returned either (no >+ * need to protect the result). >+ * @param otherInits the flow info to merge with this >+ * @return the intersection of this and otherInits. >+ */ >+abstract public UnconditionalFlowInfo mergedWith( >+ UnconditionalFlowInfo otherInits); >+ >+/** >+ * Return a copy of this unconditional flow info, deprived from its null >+ * info. {@link #DEAD_END DEAD_END} is returned unmodified. >+ * @return a copy of this unconditional flow info deprived from its null info >+ */ >+abstract public UnconditionalFlowInfo nullInfoLessUnconditionalCopy(); > > public String toString(){ > >@@ -182,5 +314,38 @@ > return super.toString(); > } > >- abstract public UnconditionalFlowInfo unconditionalInits(); >+/** >+ * Return a new flow info that holds the same information as this would after >+ * a call to unconditionalInits, but leaving this info unaffected. Moreover, >+ * the result can be modified without affecting this. >+ * @return a new flow info carrying this unconditional flow info >+ */ >+abstract public UnconditionalFlowInfo unconditionalCopy(); >+ >+/** >+ * Return a new flow info that holds the same information as this would after >+ * a call to {@link #unconditionalInits() unconditionalInits} followed by the >+ * erasure of fields specific information, but leaving this flow info unaffected. >+ * @return a new flow info carrying the unconditional flow info for local variables >+ */ >+abstract public UnconditionalFlowInfo unconditionalFieldLessCopy(); >+ >+/** >+ * Return a flow info that merges the possible paths of execution described by >+ * this flow info. In case of an unconditional flow info, return this. In case >+ * of a conditional flow info, merge branches recursively. Caveat: this may >+ * be affected, and modifying the result may affect this. >+ * @return a flow info that merges the possible paths of execution described by >+ * this >+ */ >+abstract public UnconditionalFlowInfo unconditionalInits(); >+ >+/** >+ * Return a new flow info that holds the same information as this would after >+ * a call to {@link #unconditionalInits() unconditionalInits}, but leaving >+ * this info unaffected. Side effects on the result might affect this though >+ * (consider it as read only). >+ * @return a flow info carrying this unconditional flow info >+ */ >+abstract public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect(); > } >Index: compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java,v >retrieving revision 1.15 >diff -u -r1.15 InsideSubRoutineFlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java 23 Feb 2005 02:47:29 -0000 1.15 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -47,13 +47,14 @@ > return (SubRoutineStatement)associatedNode; > } > >- public void recordReturnFrom(FlowInfo flowInfo) { >- >- if (!flowInfo.isReachable()) return; >- if (initsOnReturn == FlowInfo.DEAD_END) { >- initsOnReturn = flowInfo.copy().unconditionalInits(); >- } else { >- initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits()); >- } >+public void recordReturnFrom(UnconditionalFlowInfo flowInfo) { >+ if (!flowInfo.isReachable()) { >+ return; > } >+ if (initsOnReturn == FlowInfo.DEAD_END) { >+ initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy(); >+ } else { >+ initsOnReturn = initsOnReturn.mergedWith(flowInfo); >+ } >+} > } >Index: compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java,v >retrieving revision 1.30 >diff -u -r1.30 LoopingFlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 23 Feb 2005 02:47:29 -0000 1.30 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -28,82 +28,165 @@ > > public Label continueLabel; > public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END; >+ private UnconditionalFlowInfo upstreamNullFlowInfo; >+ private LoopingFlowContext innerFlowContexts[] = null; >+ private UnconditionalFlowInfo innerFlowInfos[] = null; >+ private int innerFlowContextsNb = 0; >+ > Reference finalAssignments[]; > VariableBinding finalVariables[]; > int assignCount = 0; > >+ LocalVariableBinding[] nullLocals; > Expression[] nullReferences; >- int[] nullStatus; >+ int[] nullCheckTypes; > int nullCount; > > Scope associatedScope; > > public LoopingFlowContext( > FlowContext parent, >+ FlowInfo upstreamNullFlowInfo, > ASTNode associatedNode, > Label breakLabel, > Label continueLabel, > Scope associatedScope) { > super(parent, associatedNode, breakLabel); >+ preemptNullDiagnostic = true; >+ // children will defer to this, which may defer to its own parent > this.continueLabel = continueLabel; > this.associatedScope = associatedScope; >+ this.upstreamNullFlowInfo = upstreamNullFlowInfo.unconditionalCopy(); > } >- >- public void complainOnDeferredChecks(BlockScope scope, FlowInfo flowInfo) { >- >- // complain on final assignments in loops >- for (int i = 0; i < assignCount; i++) { >- VariableBinding variable = finalVariables[i]; >- if (variable == null) continue; >- boolean complained = false; // remember if have complained on this final assignment >- if (variable instanceof FieldBinding) { >- if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) { >- complained = true; >- scope.problemReporter().duplicateInitializationOfBlankFinalField( >- (FieldBinding) variable, >- finalAssignments[i]); >- } >- } else { >- if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { >- complained = true; >- scope.problemReporter().duplicateInitializationOfFinalLocal( >- (LocalVariableBinding) variable, >- finalAssignments[i]); >- } >+ >+/** >+ * Perform deferred checks relative to final variables duplicate initialization >+ * of lack of initialization. >+ * @param scope the scope to which this context is associated >+ * @param flowInfo the flow info against which checks must be performed >+ */ >+public void complainOnDeferredFinalChecks(BlockScope scope, FlowInfo flowInfo) { >+ // complain on final assignments in loops >+ for (int i = 0; i < assignCount; i++) { >+ VariableBinding variable = finalVariables[i]; >+ if (variable == null) continue; >+ boolean complained = false; // remember if have complained on this final assignment >+ if (variable instanceof FieldBinding) { >+ if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) { >+ complained = true; >+ scope.problemReporter().duplicateInitializationOfBlankFinalField( >+ (FieldBinding) variable, >+ finalAssignments[i]); > } >- // any reference reported at this level is removed from the parent context where it >- // could also be reported again >- if (complained) { >- FlowContext context = parent; >- while (context != null) { >- context.removeFinalAssignmentIfAny(finalAssignments[i]); >- context = context.parent; >- } >+ } else { >+ if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { >+ complained = true; >+ scope.problemReporter().duplicateInitializationOfFinalLocal( >+ (LocalVariableBinding) variable, >+ finalAssignments[i]); > } > } >- // check inconsistent null checks >- for (int i = 0; i < nullCount; i++) { >- Expression expression = nullReferences[i]; >- if (expression == null) continue; >+ // any reference reported at this level is removed from the parent context where it >+ // could also be reported again >+ if (complained) { >+ FlowContext context = parent; >+ while (context != null) { >+ context.removeFinalAssignmentIfAny(finalAssignments[i]); >+ context = context.parent; >+ } >+ } >+ } >+} >+ >+/** >+ * Perform deferred checks relative to the null status of local variables. >+ * @param scope the scope to which this context is associated >+ * @param flowInfo the flow info against which checks must be performed >+ */ >+public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo flowInfo) { >+ for (int i = 0 ; i < this.innerFlowContextsNb ; i++) { >+ this.upstreamNullFlowInfo. >+ addPotentialNullInfoFrom( >+ this.innerFlowContexts[i].upstreamNullFlowInfo). >+ addPotentialNullInfoFrom(this.innerFlowInfos[i]); >+ } >+ this.innerFlowContextsNb = 0; >+ flowInfo = this.upstreamNullFlowInfo. >+ addPotentialNullInfoFrom( >+ flowInfo.unconditionalInitsWithoutSideEffect()); >+ if (this.deferNullDiagnostic) { >+ // check only immutable null checks on innermost looping context >+ for (int i = 0; i < this.nullCount; i++) { >+ LocalVariableBinding local = this.nullLocals[i]; >+ Expression expression = this.nullReferences[i]; > // final local variable >- LocalVariableBinding local = expression.localVariableBinding(); >- switch (nullStatus[i]) { >- case FlowInfo.NULL : >+ switch (this.nullCheckTypes[i]) { >+ case CAN_ONLY_NULL_NON_NULL : >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCannotBeNull(local, expression); >+ continue; >+ } >+ case CAN_ONLY_NULL : > if (flowInfo.isDefinitelyNull(local)) { >- nullReferences[i] = null; >- this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ continue; > } > break; >- case FlowInfo.NON_NULL : >+ case MAY_NULL: >+ if (flowInfo.isDefinitelyNull(local)) { >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ continue; >+ } >+ break; >+ default: >+ // never happens >+ } >+ this.parent.recordUsingNullReference(scope, local, expression, >+ this.nullCheckTypes[i], flowInfo); >+ } >+ } >+ else { >+ // check inconsistent null checks on outermost looping context >+ for (int i = 0; i < this.nullCount; i++) { >+ Expression expression = this.nullReferences[i]; >+ // final local variable >+ LocalVariableBinding local = this.nullLocals[i]; >+ switch (this.nullCheckTypes[i]) { >+ case CAN_ONLY_NULL_NON_NULL : > if (flowInfo.isDefinitelyNonNull(local)) { >- nullReferences[i] = null; >- this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCannotBeNull(local, expression); >+ continue; >+ } >+ case CAN_ONLY_NULL : >+ if (flowInfo.isDefinitelyNull(local)) { >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ continue; > } > break; >+ case MAY_NULL: >+ if (flowInfo.isDefinitelyNull(local)) { >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableCanOnlyBeNull(local, expression); >+ continue; >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ this.nullReferences[i] = null; >+ scope.problemReporter().localVariableMayBeNull(local, expression); >+ continue; >+ } >+ break; >+ default: >+ // never happens > } >- } >+ } > } >- >+} >+ > public Label continueLabel() { > return continueLabel; > } >@@ -125,15 +208,46 @@ > return initsOnContinue != FlowInfo.DEAD_END; > } > >- public void recordContinueFrom(FlowInfo flowInfo) { >- >- if (!flowInfo.isReachable()) return; >- if (initsOnContinue == FlowInfo.DEAD_END) { >- initsOnContinue = flowInfo.copy().unconditionalInits(); >- } else { >- initsOnContinue = initsOnContinue.mergedWith(flowInfo.copy().unconditionalInits()); >+public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { >+ if (!flowInfo.isReachable()) { >+ return; >+ } >+ if (initsOnContinue.isReachable()) { >+ initsOnContinue = initsOnContinue. >+ mergedWith(flowInfo.unconditionalInitsWithoutSideEffect()); >+ } >+ else { >+ initsOnContinue = flowInfo.unconditionalCopy(); >+ } >+ FlowContext inner = innerFlowContext; >+ while (inner != this && !(inner instanceof LoopingFlowContext)) { >+ inner = inner.parent; >+ } >+ if (inner == this) { >+ this.upstreamNullFlowInfo. >+ addPotentialNullInfoFrom( >+ flowInfo.unconditionalInitsWithoutSideEffect()); >+ } >+ else { >+ int length = 0; >+ if (this.innerFlowContexts == null) { >+ this.innerFlowContexts = new LoopingFlowContext[5]; >+ this.innerFlowInfos = new UnconditionalFlowInfo[5]; > } >+ else if (this.innerFlowContextsNb == >+ (length = this.innerFlowContexts.length) - 1) { >+ System.arraycopy(this.innerFlowContexts, 0, >+ (this.innerFlowContexts = new LoopingFlowContext[length + 5]), >+ 0, length); >+ System.arraycopy(this.innerFlowInfos, 0, >+ (this.innerFlowInfos= new UnconditionalFlowInfo[length + 5]), >+ 0, length); >+ } >+ this.innerFlowContexts[this.innerFlowContextsNb] = (LoopingFlowContext) inner; >+ this.innerFlowInfos[this.innerFlowContextsNb++] = >+ flowInfo.unconditionalInitsWithoutSideEffect(); > } >+} > > protected boolean recordFinalAssignment( > VariableBinding binding, >@@ -170,20 +284,67 @@ > return true; > } > >- protected boolean recordNullReference(Expression expression, int status) { >- if (nullCount == 0) { >- nullReferences = new Expression[5]; >- nullStatus = new int[5]; >- } else { >- if (nullCount == nullReferences.length) { >- System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount); >- System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount); >+protected void recordNullReference(LocalVariableBinding local, >+ Expression expression, int status) { >+ if (nullCount == 0) { >+ nullLocals = new LocalVariableBinding[5]; >+ nullReferences = new Expression[5]; >+ nullCheckTypes = new int[5]; >+ } >+ else if (nullCount == nullLocals.length) { >+ System.arraycopy(nullLocals, 0, >+ nullLocals = new LocalVariableBinding[nullCount * 2], 0, nullCount); >+ System.arraycopy(nullReferences, 0, >+ nullReferences = new Expression[nullCount * 2], 0, nullCount); >+ System.arraycopy(nullCheckTypes, 0, >+ nullCheckTypes = new int[nullCount * 2], 0, nullCount); >+ } >+ nullLocals[nullCount] = local; >+ nullReferences[nullCount] = expression; >+ nullCheckTypes[nullCount++] = status; >+} >+ >+public void recordUsingNullReference(Scope scope, LocalVariableBinding local, >+ Expression reference, int checkType, FlowInfo flowInfo) { >+ if (!flowInfo.isReachable() || flowInfo.isDefinitelyUnknown(local)) { >+ return; >+ } >+ switch (checkType) { >+ case CAN_ONLY_NULL_NON_NULL : >+ case CAN_ONLY_NULL: >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ if (checkType == CAN_ONLY_NULL_NON_NULL) { >+ scope.problemReporter().localVariableCannotBeNull(local, reference); >+ } >+ return; > } >- } >- nullReferences[nullCount] = expression; >- nullStatus[nullCount++] = status; >- return true; >- } >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ if (flowInfo.isPotentiallyUnknown(local)) { >+ return; >+ } >+ recordNullReference(local, reference, checkType); >+ return; >+ case MAY_NULL : >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ return; >+ } >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().localVariableCanOnlyBeNull(local, reference); >+ return; >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ scope.problemReporter().localVariableMayBeNull(local, reference); >+ return; >+ } >+ recordNullReference(local, reference, checkType); >+ return; >+ default: >+ // never happens >+ } >+} > > void removeFinalAssignmentIfAny(Reference reference) { > for (int i = 0; i < assignCount; i++) { >Index: compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java,v >retrieving revision 1.26 >diff -u -r1.26 SwitchFlowContext.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 14 Oct 2005 22:43:00 -0000 1.26 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java 28 Nov 2005 12:08:07 -0000 >@@ -42,10 +42,11 @@ > } > > public void recordBreakFrom(FlowInfo flowInfo) { >- if (initsOnBreak == FlowInfo.DEAD_END) { >- initsOnBreak = flowInfo.copy().unconditionalInits(); >- } else { >- initsOnBreak = initsOnBreak.mergedWith(flowInfo.copy().unconditionalInits()); >+ if (initsOnBreak.isReachable()) { >+ initsOnBreak = initsOnBreak.mergedWith(flowInfo.unconditionalInits()); >+ } >+ else { >+ initsOnBreak = flowInfo.unconditionalCopy(); > } > } > } >Index: compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java,v >retrieving revision 1.44 >diff -u -r1.44 UnconditionalFlowInfo.java >--- compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 18 Nov 2005 16:46:23 -0000 1.44 >+++ compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java 28 Nov 2005 12:08:08 -0000 >@@ -14,6 +14,8 @@ > import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; > import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; > import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; >+import org.eclipse.jdt.internal.compiler.lookup.TagBits; >+ > > /** > * Record initialization status during definite assignment analysis >@@ -22,807 +24,1524 @@ > */ > public class UnconditionalFlowInfo extends FlowInfo { > >- > public long definiteInits; > public long potentialInits; >- public long extraDefiniteInits[]; >- public long extraPotentialInits[]; > >- public long definiteNulls; >- public long definiteNonNulls; >- public long extraDefiniteNulls[]; >- public long extraDefiniteNonNulls[]; >- >- public int reachMode; // by default >+ public long nullAssignmentStatusBit1; >+ public long nullAssignmentStatusBit2; >+ // 0 0 is potential (bit 1 is leftmost here) >+ // 1 0 is assigned >+ // 0 1 is protected null (aka if (o == null) { // here o protected null...) >+ // 1 1 is protected non null >+ public long nullAssignmentValueBit1; >+ public long nullAssignmentValueBit2; >+ // information only relevant for potential and assigned >+ // 0 0 is start -- nothing known at all >+ // 0 1 is assigned non null or potential anything but null >+ // 1 0 is assigned null or potential null >+ // 1 1 is potential null and potential anything but null or definite unknown >+ >+ public static final int extraLength = 6; >+ public long extra[][]; >+ // extra bit fields for larger numbers of fields/variables >+ // extra[0] holds definiteInits values, extra[1] potentialInits, etc. >+ // lifecycle is extra == null or else all extra[]'s are allocated >+ // arrays which have the same size > >- public int maxFieldCount; >+ public int maxFieldCount; // limit between fields and locals > > // Constants > public static final int BitCacheSize = 64; // 64 bits in a long. > >- UnconditionalFlowInfo() { >- this.reachMode = REACHABLE; >- } >- >- // unions of both sets of initialization - used for try/finally >- public FlowInfo addInitializationsFrom(FlowInfo inits) { >+public FlowInfo addInitializationsFrom(FlowInfo inits) { >+ if (this == DEAD_END) >+ return this; >+ if (inits == DEAD_END) >+ return this; >+ UnconditionalFlowInfo otherInits = inits.unconditionalInits(); > >- if (this == DEAD_END) >- return this; >- >- UnconditionalFlowInfo otherInits = inits.unconditionalInits(); >- if (otherInits == DEAD_END) >- return this; >- >- // union of definitely assigned variables, >- definiteInits |= otherInits.definiteInits; >- // union of potentially set ones >- potentialInits |= otherInits.potentialInits; >- >- // union of definitely null variables, >- definiteNulls = (definiteNulls | otherInits.definiteNulls) & ~otherInits.definiteNonNulls; >- // union of definitely non null variables, >- definiteNonNulls = (definiteNonNulls | otherInits.definiteNonNulls) & ~otherInits.definiteNulls; >- // fix-up null/non-null infos since cannot overlap: <defN1:0,defNoN1:1> + <defN2:1,defNoN2:0> --> <defN:0,defNon:0> >- >- // treating extra storage >- if (extraDefiniteInits != null) { >- if (otherInits.extraDefiniteInits != null) { >+ // union of definitely assigned variables, >+ this.definiteInits |= otherInits.definiteInits; >+ // union of potentially set ones >+ this.potentialInits |= otherInits.potentialInits; >+ // combine null information >+ // note: we may have both forms of protection (null and non null) >+ // coming with otherInits, because of loops >+ boolean considerNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0; >+ long a1, na1, a2, na2, a3, a4, na4, b1, b2, nb2, b3, nb3, b4, nb4; >+ if (considerNulls) { >+ if ((this.tagBits & NULL_FLAG_MASK) == 0) { >+ this.nullAssignmentStatusBit1 = otherInits.nullAssignmentStatusBit1; >+ this.nullAssignmentStatusBit2 = otherInits.nullAssignmentStatusBit2; >+ this.nullAssignmentValueBit1 = otherInits.nullAssignmentValueBit1; >+ this.nullAssignmentValueBit2 = otherInits.nullAssignmentValueBit2; >+ } >+ else { >+ this.nullAssignmentStatusBit1 = >+ (b1 = otherInits.nullAssignmentStatusBit1) | >+ (a1 = this.nullAssignmentStatusBit1) & >+ ((nb2 = ~(b2 = otherInits.nullAssignmentStatusBit2)) & >+ (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & >+ ((nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) | >+ ((a2 = this.nullAssignmentStatusBit2) ^ >+ (a4 = this.nullAssignmentValueBit2))) | >+ nb4 & (na2 = ~a2) & (na4 = ~a4)); >+ this.nullAssignmentStatusBit2 = >+ b1 & b2 | >+ ~b1 & (((na1 = ~a1) | a4) & b2 | >+ a2 & (b2 | >+ a1 & (na4 = ~a4) & nb2 & nb3 | >+ (~(a3 = this.nullAssignmentValueBit1) & nb3 | na1 & na4) & nb4)); >+ this.nullAssignmentValueBit1 = >+ nb2 & b3 | >+ ~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) | >+ a1 & na2 & a3 & nb2 | >+ (a1 | a2 | na4) & b3); >+ this.nullAssignmentValueBit2 = >+ b4 | >+ a4 & (nb2 & nb3 | ~(b1 ^ b2)); >+ } >+ this.tagBits |= NULL_FLAG_MASK; // in all cases - avoid forgetting extras >+ } >+ // treating extra storage >+ if (this.extra != null || otherInits.extra != null) { >+ int mergeLimit = 0, copyLimit = 0; >+ if (this.extra != null) { >+ if (otherInits.extra != null) { > // both sides have extra storage >- int i = 0, length, otherLength; >- if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { >+ int length, otherLength; >+ if ((length = this.extra[0].length) < >+ (otherLength = otherInits.extra[0].length)) { > // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) >- System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length); >- System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length); >- System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, length); >- System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, length); >- for (; i < length; i++) { >- extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i]; >- extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i]; >- extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i]; >- } >- for (; i < otherLength; i++) { >- extraPotentialInits[i] = otherInits.extraPotentialInits[i]; >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[otherLength]), 0, length); > } >+ mergeLimit = length; >+ copyLimit = otherLength; > } else { > // current storage is longer >- for (; i < otherLength; i++) { >- extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i]; >- extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i]; >- extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i]; >- } >- for (; i < length; i++) { >- extraDefiniteInits[i] = 0; >- extraDefiniteNulls[i] = 0; >- extraDefiniteNonNulls[i] = 0; >+ mergeLimit = otherLength; >+ } >+ } >+ } >+ else if (otherInits.extra != null) { >+ // no storage here, but other has extra storage. >+ // shortcut regular copy because array copy is better >+ int otherLength; >+ this.extra = new long[extraLength][]; >+ System.arraycopy(otherInits.extra[0], 0, >+ (this.extra[0] = new long[otherLength = >+ otherInits.extra[0].length]), 0, otherLength); >+ System.arraycopy(otherInits.extra[1], 0, >+ (this.extra[1] = new long[otherLength]), 0, otherLength); >+ if (considerNulls) { >+ for (int j = 2; j < extraLength; j++) { >+ System.arraycopy(otherInits.extra[j], 0, >+ (this.extra[j] = new long[otherLength]), 0, otherLength); >+ } >+ } >+ else { >+ for (int j = 2; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; >+ } >+ } >+ } >+ int i = 0; >+ for (; i < mergeLimit; i++) { >+ this.extra[0][i] |= otherInits.extra[0][i]; >+ this.extra[1][i] |= otherInits.extra[1][i]; >+ if (considerNulls) { // could consider pushing the test outside the loop >+ if (this.extra[2][i] == 0 && >+ this.extra[3][i] == 0 && >+ this.extra[4][i] == 0 && >+ this.extra[5][i] == 0) { >+ for (int j = 2; j < extraLength; j++) { >+ this.extra[j][i] = otherInits.extra[j][i]; > } > } >- } else { >- // no extra storage on otherInits >+ else { >+ this.extra[2][i] = >+ (b1 = otherInits.extra[2][i]) | >+ (a1 = this.extra[2][i]) & >+ ((nb2 = ~(b2 = otherInits.extra[3][i])) & >+ (nb3 = ~(b3 = otherInits.extra[4][i])) & >+ ((nb4 = ~(b4 = otherInits.extra[5][i])) | >+ ((a2 = this.extra[3][i]) ^ >+ (a4 = this.extra[5][i]))) | >+ nb4 & (na2 = ~a2) & (na4 = ~a4)); >+ this.extra[3][i] = >+ b1 & b2 | >+ ~b1 & (((na1 = ~a1) | a4) & b2 | >+ a2 & (b2 | >+ a1 & (na4 = ~a4) & nb2 & nb3 | >+ (~(a3 = this.extra[4][i]) & nb3 | na1 & na4) & nb4)); >+ this.extra[4][i] = >+ nb2 & b3 | >+ ~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) | >+ a1 & na2 & a3 & nb2 | >+ (a1 | a2 | na4) & b3); >+ this.extra[5][i] = >+ b4 | >+ a4 & (nb2 & nb3 | ~(b1 ^ b2)); >+ } > } >- } else >- if (otherInits.extraDefiniteInits != null) { >- // no storage here, but other has extra storage. >- int otherLength; >- System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength); >- System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength); >- System.arraycopy(otherInits.extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, otherLength); >- System.arraycopy(otherInits.extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, otherLength); >+ } >+ for (; i < copyLimit; i++) { >+ this.extra[0][i] = otherInits.extra[0][i]; >+ this.extra[1][i] = otherInits.extra[1][i]; >+ if (considerNulls) { >+ for (int j = 2; j < extraLength; j++) { >+ this.extra[j][i] = otherInits.extra[j][i]; >+ } > } >- return this; >+ } > } >+ return this; >+} > >- // unions of both sets of initialization - used for try/finally >- public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) { >- >- if (this == DEAD_END){ >- return this; >+public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) { >+ if (this == DEAD_END){ >+ return this; >+ } >+ if (inits == DEAD_END){ >+ return this; >+ } >+ UnconditionalFlowInfo otherInits = inits.unconditionalInits(); >+ // union of potentially set ones >+ this.potentialInits |= otherInits.potentialInits; >+ // treating extra storage >+ if (this.extra != null) { >+ if (otherInits.extra != null) { >+ // both sides have extra storage >+ int i = 0, length, otherLength; >+ if ((length = this.extra[0].length) < (otherLength = otherInits.extra[0].length)) { >+ // current storage is shorter -> grow current >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[otherLength]), 0, length); >+ } >+ for (; i < length; i++) { >+ this.extra[1][i] |= otherInits.extra[1][i]; >+ } >+ for (; i < otherLength; i++) { >+ this.extra[1][i] = otherInits.extra[1][i]; >+ } >+ } >+ else { >+ // current storage is longer >+ for (; i < otherLength; i++) { >+ this.extra[1][i] |= otherInits.extra[1][i]; >+ } >+ } >+ } >+ } >+ else if (otherInits.extra != null) { >+ // no storage here, but other has extra storage. >+ int otherLength = otherInits.extra[0].length; >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; > } >+ System.arraycopy(otherInits.extra[1], 0, this.extra[1], 0, >+ otherLength); >+ } >+ this.addPotentialNullInfoFrom(otherInits); >+ return this; >+} > >- UnconditionalFlowInfo otherInits = inits.unconditionalInits(); >- if (otherInits == DEAD_END){ >- return this; >- } >- // union of potentially set ones >- this.potentialInits |= otherInits.potentialInits; >- // also merge null check information (affected by potential inits) >- this.definiteNulls &= otherInits.definiteNulls; >- this.definiteNonNulls &= otherInits.definiteNonNulls; >- >- // treating extra storage >- if (this.extraDefiniteInits != null) { >- if (otherInits.extraDefiniteInits != null) { >- // both sides have extra storage >- int i = 0, length, otherLength; >- if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { >- // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) >- System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length); >- System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length); >- System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length); >- System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length); >- while (i < length) { >- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; >- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; >+/** >+ * Compose other inits over this flow info, then return this. The operation >+ * semantics are to wave into this flow info the consequences upon null >+ * information of a possible path into the operations that resulted into >+ * otherInits. The fact that this path may be left unexecuted under peculiar >+ * conditions results into less specific results than >+ * {@link #addInitializationsFrom(FlowInfo) addInitializationsFrom}; moreover, >+ * only the null information is affected. >+ * @param otherInits other null inits to compose over this >+ * @return this, modified according to otherInits information >+ */ >+public UnconditionalFlowInfo addPotentialNullInfoFrom( >+ UnconditionalFlowInfo otherInits) { >+ if ((this.tagBits & UNREACHABLE) != 0 || >+ (otherInits.tagBits & UNREACHABLE) != 0 || >+ (otherInits.tagBits & NULL_FLAG_MASK) == 0) { >+ return this; >+ } >+ // if we get here, otherInits has some null info >+ boolean thisHasNulls = (this.tagBits & NULL_FLAG_MASK) != 0; >+ if (thisHasNulls) { >+ long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4; >+ this.nullAssignmentStatusBit1 = >+ ((a1 = this.nullAssignmentStatusBit1) & >+ (na4 = ~(a4 = this.nullAssignmentValueBit2)) & >+ ((na3 = ~(a3 = this.nullAssignmentValueBit1)) | >+ (a2 = this.nullAssignmentStatusBit2)) | >+ a2 & na3 & a4) & >+ (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & >+ ((b2 = otherInits.nullAssignmentStatusBit2) | >+ (nb4 = ~(b4 = otherInits.nullAssignmentValueBit2))) | >+ a1 & (na2 = ~a2) & >+ (a4 & ((nb1 = ~(b1 = otherInits.nullAssignmentStatusBit1)) & >+ nb3 | b1 & >+ (b4 | b2)) | >+ na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | >+ b1 & nb4 & (nb2 | nb3))); >+ this.nullAssignmentStatusBit2 = >+ a2 & (~a1 & na4 & nb4 | >+ a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) | >+ b1 & (nb4 |b2 & b4))); >+ this.nullAssignmentValueBit1 = >+ a3 | >+ b1 & nb2 & nb4 | >+ nb1 & b3 | >+ a1 & na2 & (b1 & b3 | nb1 & b4); >+// b1 & (~b2 & ~b4 | a1 & ~a2 & b3) | >+// ~b1 & (b3 | a1 & ~a2 & b4); -- same op nb >+ this.nullAssignmentValueBit2 = >+ a4 & (na2 | a2 & na3) | >+ b4 & (nb2 | b2 & nb3); >+ // extra storage management >+ if (otherInits.extra != null) { >+ int mergeLimit = 0, copyLimit = 0; >+ int otherLength = otherInits.extra[0].length; >+ if (this.extra == null) { >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; >+ } >+ copyLimit = otherLength; >+ } >+ else { >+ mergeLimit = otherLength; >+ if (mergeLimit > this.extra[0].length) { >+ copyLimit = mergeLimit; >+ mergeLimit = this.extra[0].length; >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ this.extra[j] = new long[otherLength], 0, >+ mergeLimit); > } >- while (i < otherLength) { >- this.extraPotentialInits[i] = otherInits.extraPotentialInits[i]; >- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; >- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; >+ } >+ int i; >+ for (i = 0; i < mergeLimit; i++) { >+ this.extra[2][i] = >+ ((a1 = this.extra[2][i]) & >+ (na4 = ~(a4 = this.extra[5][i])) & >+ ((na3 = ~(a3 = this.extra[4][i])) | >+ (a2 = this.extra[3][i])) | >+ a2 & na3 & a4) & >+ (nb3 = ~(b3 = otherInits.extra[4][i])) & >+ ((b2 = otherInits.extra[3][i]) | >+ (nb4 = ~(b4 = otherInits.extra[5][i]))) | >+ a1 & (na2 = ~a2) & >+ (a4 & ((nb1 = ~(b1 = otherInits.extra[2][i])) & >+ nb3 | b1 & >+ (b4 | b2)) | >+ na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | >+ b1 & nb4 & (nb2 | nb3))); >+ this.extra[3][i] = >+ a2 & (~a1 & na4 & nb4 | >+ a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) | >+ b1 & (nb4 |b2 & b4))); >+ this.extra[4][i] = >+ a3 | >+ b1 & nb2 & nb4 | >+ nb1 & b3 | >+ a1 & na2 & (b1 & b3 | nb1 & b4); >+ this.extra[5][i] = >+ a4 & (na2 | a2 & na3) | >+ b4 & (nb2 | b2 & nb3); >+ } >+ for (; i < copyLimit; i++) { >+ if (otherInits.extra[4][i] != 0 || >+ otherInits.extra[5][i] != 0) { >+ this.tagBits |= NULL_FLAG_MASK; >+ this.extra[4][i] = >+ otherInits.extra[4][i] & >+ ~(otherInits.extra[2][i] & >+ ~otherInits.extra[3][i] & >+ otherInits.extra[5][i]); >+ this.extra[5][i] = >+ otherInits.extra[5][i]; > } >- } else { >- // current storage is longer >- while (i < otherLength) { >- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; >- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; >+ } >+ } >+ } >+ } >+ else { >+ if (otherInits.nullAssignmentValueBit1 != 0 || >+ otherInits.nullAssignmentValueBit2 != 0) { >+ // add potential values >+ this.nullAssignmentValueBit1 = >+ otherInits.nullAssignmentValueBit1 & >+ ~(otherInits.nullAssignmentStatusBit1 & >+ ~otherInits.nullAssignmentStatusBit2 & >+ otherInits.nullAssignmentValueBit2); // exclude assigned unknown >+ this.nullAssignmentValueBit2 = >+ otherInits.nullAssignmentValueBit2; >+ thisHasNulls = >+ this.nullAssignmentValueBit1 != 0 || >+ this.nullAssignmentValueBit2 != 0; >+ } >+ // extra storage management >+ if (otherInits.extra != null) { >+ int mergeLimit = 0, copyLimit = 0; >+ int otherLength = otherInits.extra[0].length; >+ if (this.extra == null) { >+ copyLimit = otherLength; >+ // cannot happen when called from addPotentialInitializationsFrom >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; >+ } >+ } >+ else { >+ mergeLimit = otherLength; >+ if (mergeLimit > this.extra[0].length) { >+ copyLimit = mergeLimit; >+ mergeLimit = this.extra[0].length; >+ System.arraycopy(this.extra[0], 0, >+ this.extra[0] = new long[otherLength], 0, >+ mergeLimit); >+ System.arraycopy(this.extra[1], 0, >+ this.extra[1] = new long[otherLength], 0, >+ mergeLimit); >+ for (int j = 2; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; > } > } > } >- } else >- if (otherInits.extraDefiniteInits != null) { >- // no storage here, but other has extra storage. >- int otherLength; >- this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]; >- System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength); >- this.extraDefiniteNulls = new long[otherLength]; >- this.extraDefiniteNonNulls = new long[otherLength]; >+ int i; >+ for (i = 0; i < mergeLimit; i++) { >+ if (otherInits.extra[4][i] != 0 || >+ otherInits.extra[5][i] != 0) { >+ this.extra[4][i] |= >+ otherInits.extra[4][i] & >+ ~(otherInits.extra[2][i] & >+ ~otherInits.extra[3][i] & >+ otherInits.extra[5][i]); >+ this.extra[5][i] |= >+ otherInits.extra[5][i]; >+ thisHasNulls = thisHasNulls || >+ this.extra[4][i] != 0 || >+ this.extra[5][i] != 0; >+ } >+ } >+ for (; i < copyLimit; i++) { >+ if (otherInits.extra[4][i] != 0 || >+ otherInits.extra[5][i] != 0) { >+ this.extra[4][i] = >+ otherInits.extra[4][i] & >+ ~(otherInits.extra[2][i] & >+ ~otherInits.extra[3][i] & >+ otherInits.extra[5][i]); >+ this.extra[5][i] = >+ otherInits.extra[5][i]; >+ thisHasNulls = thisHasNulls || >+ this.extra[4][i] != 0 || >+ this.extra[5][i] != 0; >+ } > } >- return this; >- } >- >- /** >- * Answers a copy of the current instance >- */ >- public FlowInfo copy() { >- >- // do not clone the DeadEnd >- if (this == DEAD_END) >- return this; >- >- // look for an unused preallocated object >- UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); >- >- // copy slots >- copy.definiteInits = this.definiteInits; >- copy.potentialInits = this.potentialInits; >- copy.definiteNulls = this.definiteNulls; >- copy.definiteNonNulls = this.definiteNonNulls; >- copy.reachMode = this.reachMode; >- copy.maxFieldCount = this.maxFieldCount; >- >- if (this.extraDefiniteInits != null) { >- int length; >- System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[length = extraDefiniteInits.length]), 0, length); >- System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length); >- System.arraycopy(this.extraDefiniteNulls, 0, (copy.extraDefiniteNulls = new long[length]), 0, length); >- System.arraycopy(this.extraDefiniteNonNulls, 0, (copy.extraDefiniteNonNulls = new long[length]), 0, length); > } >- return copy; > } >- >- public UnconditionalFlowInfo discardFieldInitializations(){ >- >- int limit = this.maxFieldCount; >- >- if (limit < BitCacheSize) { >- long mask = (1L << limit)-1; >- this.definiteInits &= ~mask; >- this.potentialInits &= ~mask; >- this.definiteNulls &= ~mask; >- this.definiteNonNulls &= ~mask; >- return this; >- } >- >- this.definiteInits = 0; >- this.potentialInits = 0; >- this.definiteNulls = 0; >- this.definiteNonNulls = 0; >- >- // use extra vector >- if (extraDefiniteInits == null) { >- return this; // if vector not yet allocated, then not initialized >- } >- int vectorIndex, length = this.extraDefiniteInits.length; >- if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { >- return this; // not enough room yet >- } >- for (int i = 0; i < vectorIndex; i++) { >- this.extraDefiniteInits[i] = 0L; >- this.extraPotentialInits[i] = 0L; >- this.extraDefiniteNulls[i] = 0L; >- this.extraDefiniteNonNulls[i] = 0L; >- } >- long mask = (1L << (limit % BitCacheSize))-1; >- this.extraDefiniteInits[vectorIndex] &= ~mask; >- this.extraPotentialInits[vectorIndex] &= ~mask; >- this.extraDefiniteNulls[vectorIndex] &= ~mask; >- this.extraDefiniteNonNulls[vectorIndex] &= ~mask; >- return this; >+ if (thisHasNulls) { >+ this.tagBits |= NULL_FLAG_MASK; >+ } >+ else { >+ this.tagBits &= NULL_FLAG_MASK; > } >+ return this; >+} > >- public UnconditionalFlowInfo discardNonFieldInitializations(){ >- >- int limit = this.maxFieldCount; >- >- if (limit < BitCacheSize) { >- long mask = (1L << limit)-1; >- this.definiteInits &= mask; >- this.potentialInits &= mask; >- this.definiteNulls &= mask; >- this.definiteNonNulls &= mask; >- return this; >- } >- // use extra vector >- if (extraDefiniteInits == null) { >- return this; // if vector not yet allocated, then not initialized >- } >- int vectorIndex, length = this.extraDefiniteInits.length; >- if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { >- return this; // not enough room yet >- } >- long mask = (1L << (limit % BitCacheSize))-1; >- this.extraDefiniteInits[vectorIndex] &= mask; >- this.extraPotentialInits[vectorIndex] &= mask; >- this.extraDefiniteNulls[vectorIndex] &= mask; >- this.extraDefiniteNonNulls[vectorIndex] &= mask; >- for (int i = vectorIndex+1; i < length; i++) { >- this.extraDefiniteInits[i] = 0L; >- this.extraPotentialInits[i] = 0L; >- this.extraDefiniteNulls[i] = 0L; >- this.extraDefiniteNonNulls[i] = 0L; >- } >+public FlowInfo copy() { >+ // do not clone the DeadEnd >+ if (this == DEAD_END) { > return this; > } >- >- public UnconditionalFlowInfo discardNullRelatedInitializations(){ >- >- this.definiteNulls = 0; >- this.definiteNonNulls = 0; >- >- int length = this.extraDefiniteInits == null ? 0 : this.extraDefiniteInits.length; >- for (int i = 0; i < length; i++) { >- this.extraDefiniteNulls[i] = 0L; >- this.extraDefiniteNonNulls[i] = 0L; >+ UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); >+ // copy slots >+ copy.definiteInits = this.definiteInits; >+ copy.potentialInits = this.potentialInits; >+ boolean hasNullInfo = (this.tagBits & NULL_FLAG_MASK) != 0; >+ if (hasNullInfo) { >+ copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1; >+ copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2; >+ copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1; >+ copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2; >+ } >+ copy.tagBits = this.tagBits; >+ copy.maxFieldCount = this.maxFieldCount; >+ if (this.extra != null) { >+ int length; >+ copy.extra = new long[extraLength][]; >+ System.arraycopy(this.extra[0], 0, >+ (copy.extra[0] = new long[length = this.extra[0].length]), 0, >+ length); >+ System.arraycopy(this.extra[1], 0, >+ (copy.extra[1] = new long[length]), 0, length); >+ if (hasNullInfo) { >+ for (int j = 2; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (copy.extra[j] = new long[length]), 0, length); >+ } >+ } >+ else { >+ for (int j = 2; j < extraLength; j++) { >+ copy.extra[j] = new long[length]; >+ } > } >- return this; > } >+ return copy; >+} > >- public FlowInfo initsWhenFalse() { >- >- return this; >- } >- >- public FlowInfo initsWhenTrue() { >- >- return this; >+/** >+ * Remove local variables information from this flow info and return this. >+ * @return this, deprived from any local variable information >+ */ >+public UnconditionalFlowInfo discardNonFieldInitializations() { >+ int limit = this.maxFieldCount; >+ if (limit < BitCacheSize) { >+ long mask = (1L << limit)-1; >+ this.definiteInits &= mask; >+ this.potentialInits &= mask; >+ this.nullAssignmentStatusBit1 &= mask; >+ this.nullAssignmentStatusBit2 &= mask; >+ this.nullAssignmentValueBit1 &= mask; >+ this.nullAssignmentValueBit2 &= mask; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return this; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex, length = this.extra[0].length; >+ if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) { >+ return this; // not enough room yet > } >- >- /** >- * Check status of definite assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private boolean isDefinitelyAssigned(int position) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // id is zero-based >- if (position < BitCacheSize) { >- return (definiteInits & (1L << position)) != 0; // use bits >+ if (vectorIndex >= 0) { >+ // else we only have complete non field array items left >+ long mask = (1L << (limit % BitCacheSize))-1; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j][vectorIndex] &= mask; > } >- // use extra vector >- if (extraDefiniteInits == null) >- return false; // if vector not yet allocated, then not initialized >- int vectorIndex; >- if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length) >- return false; // if not enough room in vector, then not initialized >- return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; > } >- >- /** >- * Check status of definite non-null assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private boolean isDefinitelyNonNull(int position) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // id is zero-based >- if (position < BitCacheSize) { >- return (definiteNonNulls & (1L << position)) != 0; // use bits >+ for (int i = vectorIndex + 1; i < length; i++) { >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j][i] = 0; > } >- // use extra vector >- if (extraDefiniteNonNulls == null) >- return false; // if vector not yet allocated, then not initialized >- int vectorIndex; >- if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNonNulls.length) >- return false; // if not enough room in vector, then not initialized >- return ((extraDefiniteNonNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; >- } >- >- /** >- * Check status of definite null assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private boolean isDefinitelyNull(int position) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // id is zero-based >- if (position < BitCacheSize) { >- return (definiteNulls & (1L << position)) != 0; // use bits >- } >- // use extra vector >- if (extraDefiniteNulls == null) >- return false; // if vector not yet allocated, then not initialized >- int vectorIndex; >- if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNulls.length) >- return false; // if not enough room in vector, then not initialized >- return ((extraDefiniteNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; >- } >- >- /** >- * Check status of definite assignment for a field. >- */ >- final public boolean isDefinitelyAssigned(FieldBinding field) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return true; >- return isDefinitelyAssigned(field.id); > } >- >- /** >- * Check status of definite assignment for a local. >- */ >- final public boolean isDefinitelyAssigned(LocalVariableBinding local) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return true; >- >- // final constants are inlined, and thus considered as always initialized >- if (local.constant() != Constant.NotAConstant) { >- return true; >- } >- return isDefinitelyAssigned(local.id + maxFieldCount); >+ return this; >+} >+ >+public FlowInfo initsWhenFalse() { >+ return this; >+} >+ >+public FlowInfo initsWhenTrue() { >+ return this; >+} >+ >+/** >+ * Check status of definite assignment at a given position. >+ * It deals with the dual representation of the InitializationInfo2: >+ * bits for the first 64 entries, then an array of booleans. >+ */ >+final private boolean isDefinitelyAssigned(int position) { >+ if (position < BitCacheSize) { >+ // use bits >+ return (this.definiteInits & (1L << position)) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) >+ return false; // if vector not yet allocated, then not initialized >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >+ >= this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized > } >- >- /** >- * Check status of definite non-null assignment for a field. >- */ >- final public boolean isDefinitelyNonNull(FieldBinding field) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return false; >- return isDefinitelyNonNull(field.id); >+ return ((this.extra[0][vectorIndex]) & >+ (1L << (position % BitCacheSize))) != 0; >+} >+ >+final public boolean isDefinitelyAssigned(FieldBinding field) { >+ // Dependant of CodeStream.isDefinitelyAssigned(..) >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0) { >+ return true; > } >- >- /** >- * Check status of definite non-null assignment for a local. >- */ >- final public boolean isDefinitelyNonNull(LocalVariableBinding local) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return false; >- // final constants are inlined, and thus considered as always initialized >- if (local.constant() != Constant.NotAConstant) { >- return true; >- } >- return isDefinitelyNonNull(local.id + maxFieldCount); >- } >- >- /** >- * Check status of definite null assignment for a field. >- */ >- final public boolean isDefinitelyNull(FieldBinding field) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return false; >- return isDefinitelyNull(field.id); >+ return isDefinitelyAssigned(field.id); >+} >+ >+final public boolean isDefinitelyAssigned(LocalVariableBinding local) { >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0) { >+ return true; >+ } >+ // final constants are inlined, and thus considered as always initialized >+ if (local.constant() != Constant.NotAConstant) { >+ return true; > } >- >- /** >- * Check status of definite null assignment for a local. >- */ >- final public boolean isDefinitelyNull(LocalVariableBinding local) { >- >- // Dependant of CodeStream.isDefinitelyAssigned(..) >- // We do not want to complain in unreachable code >- if ((this.reachMode & UNREACHABLE) != 0) >- return false; >- return isDefinitelyNull(local.id + maxFieldCount); >+ return isDefinitelyAssigned(local.id + this.maxFieldCount); >+} >+ >+final public boolean isDefinitelyNonNull(LocalVariableBinding local) { >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0 || >+ (this.tagBits & NULL_FLAG_MASK) == 0) { >+ return false; >+ } >+ if ((local.type.tagBits & TagBits.IsBaseType) != 0 || >+ local.constant() != Constant.NotAConstant) { >+ return true; >+ } >+ int position = local.id + this.maxFieldCount; >+ long mask; >+ if (position < BitCacheSize) { // use bits >+ return >+ (this.nullAssignmentStatusBit2 & >+ (mask = 1L << position)) != 0 ? >+ (this.nullAssignmentStatusBit1 & mask) != 0 : >+ (this.nullAssignmentStatusBit1 & >+ this.nullAssignmentValueBit2 & mask) != 0 && >+ (this.nullAssignmentValueBit1 & mask) == 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >+ >= this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return >+ (this.extra[3][vectorIndex] & >+ (mask = 1L << (position % BitCacheSize))) != 0 ? >+ (this.extra[2][vectorIndex] & mask) != 0 : >+ (this.extra[2][vectorIndex] & >+ this.extra[5][vectorIndex] & mask) != 0 && >+ (this.extra[4][vectorIndex] & mask) == 0; >+} >+ >+final public boolean isDefinitelyNull(LocalVariableBinding local) { >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0 || >+ (this.tagBits & NULL_FLAG_MASK) == 0 || >+ (local.type.tagBits & TagBits.IsBaseType) != 0) { >+ return false; >+ } >+ int position = local.id + this.maxFieldCount; >+ long mask; >+ if (position < BitCacheSize) { // use bits >+ return >+ (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? >+ (this.nullAssignmentStatusBit1 & mask) == 0 : >+ (this.nullAssignmentStatusBit1 & >+ this.nullAssignmentValueBit1 & mask) != 0 && >+ (this.nullAssignmentValueBit2 & mask) == 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return >+ (this.extra[3][vectorIndex] & >+ (mask = 1L << (position % BitCacheSize))) != 0 ? >+ (this.extra[2][vectorIndex] & mask) == 0 : >+ (this.extra[2][vectorIndex] & >+ this.extra[4][vectorIndex] & mask) != 0 && >+ (this.extra[5][vectorIndex] & mask) == 0; >+} >+ >+final public boolean isDefinitelyUnknown(LocalVariableBinding local) { >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0 || >+ (this.tagBits & NULL_FLAG_MASK) == 0) { >+ return false; >+ } >+ int position = local.id + this.maxFieldCount; >+ long mask; >+ if (position < BitCacheSize) { // use bits >+ return >+ (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? >+ false : >+ (this.nullAssignmentStatusBit1 & >+ this.nullAssignmentValueBit1 & >+ this.nullAssignmentValueBit2 & mask) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return >+ (this.extra[3][vectorIndex] & >+ (mask = 1L << (position % BitCacheSize))) != 0 ? >+ false : >+ (this.extra[2][vectorIndex] & >+ this.extra[4][vectorIndex] & >+ this.extra[5][vectorIndex] & >+ mask) != 0; >+} >+ >+/** >+ * Check status of potential assignment at a given position. >+ * It deals with the dual representation of the InitializationInfo3: >+ * bits for the first 64 entries, then an array of booleans. >+ */ >+final private boolean isPotentiallyAssigned(int position) { >+ // id is zero-based >+ if (position < BitCacheSize) { >+ // use bits >+ return (this.potentialInits & (1L << position)) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >+ >= this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized > } >+ return ((this.extra[1][vectorIndex]) & >+ (1L << (position % BitCacheSize))) != 0; >+} >+ >+/** >+ * Check status of definite assignment for a field. >+ */ >+final public boolean isPotentiallyAssigned(FieldBinding field) { >+ return isPotentiallyAssigned(field.id); >+} > >- public boolean isReachable() { >- >- return this.reachMode == REACHABLE; >+/** >+ * Check status of potential assignment for a local. >+ */ >+final public boolean isPotentiallyAssigned(LocalVariableBinding local) { >+ // final constants are inlined, and thus considered as always initialized >+ if (local.constant() != Constant.NotAConstant) { >+ return true; > } >- >- /** >- * Check status of potential assignment at a given position. >- * It deals with the dual representation of the InitializationInfo3: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private boolean isPotentiallyAssigned(int position) { >- >- // id is zero-based >- if (position < BitCacheSize) { >+ return isPotentiallyAssigned(local.id + this.maxFieldCount); >+} >+ >+final public boolean isPotentiallyNull(LocalVariableBinding local) { >+ if ((this.tagBits & NULL_FLAG_MASK) == 0 || >+ (local.type.tagBits & TagBits.IsBaseType) != 0) { >+ return false; >+ } >+ int position; >+ long mask; >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { >+ // use bits >+ return >+ (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? >+ (this.nullAssignmentStatusBit1 & mask) == 0 : // protected null >+ (this.nullAssignmentValueBit1 & mask) != 0 && // null bit set and >+ ((this.nullAssignmentStatusBit1 & mask) == 0 || // (potential or >+ (this.nullAssignmentValueBit2 & mask) == 0); >+ // assigned, but not unknown) >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return >+ (this.extra[3][vectorIndex] & >+ (mask = 1L << (position % BitCacheSize))) != 0 ? >+ (this.extra[2][vectorIndex] & mask) == 0 : >+ (this.extra[4][vectorIndex] & mask) != 0 && >+ ((this.extra[2][vectorIndex] & mask) == 0 || >+ (this.extra[5][vectorIndex] & mask) == 0); >+} >+ >+final public boolean isPotentiallyUnknown(LocalVariableBinding local) { >+ // do not want to complain in unreachable code >+ if ((this.tagBits & UNREACHABLE) != 0 || >+ (this.tagBits & NULL_FLAG_MASK) == 0) { >+ return false; >+ } >+ int position = local.id + this.maxFieldCount; >+ long mask; >+ if (position < BitCacheSize) { // use bits >+ return >+ (this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ? >+ false : >+ ((this.nullAssignmentStatusBit1 & >+ this.nullAssignmentValueBit1 | >+ ~this.nullAssignmentStatusBit1 & >+ ~this.nullAssignmentValueBit1) & >+ this.nullAssignmentValueBit2 & mask) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return >+ (this.extra[3][vectorIndex] & >+ (mask = 1L << (position % BitCacheSize))) != 0 ? >+ false : >+ ((this.extra[2][vectorIndex] & >+ this.extra[4][vectorIndex] | >+ ~this.extra[2][vectorIndex] & >+ ~this.extra[4][vectorIndex]) & >+ this.extra[5][vectorIndex] & >+ mask) != 0; >+} >+ >+final public boolean isProtectedNonNull(LocalVariableBinding local) { >+ if ((this.tagBits & NULL_FLAG_MASK) == 0 || >+ (local.type.tagBits & TagBits.IsBaseType) != 0) { >+ return false; >+ } >+ int position; >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { >+ // use bits >+ return (this.nullAssignmentStatusBit1 & >+ this.nullAssignmentStatusBit2 & (1L << position)) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return (this.extra[4][vectorIndex] & >+ this.extra[5][vectorIndex] & >+ (1L << (position % BitCacheSize))) != 0; >+} >+ >+final public boolean isProtectedNull(LocalVariableBinding local) { >+ if ((this.tagBits & NULL_FLAG_MASK) == 0 || >+ (local.type.tagBits & TagBits.IsBaseType) != 0) { >+ return false; >+ } >+ int position; >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { >+ // use bits >+ return (~this.nullAssignmentStatusBit1 & >+ this.nullAssignmentStatusBit2 & (1L << position)) != 0; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return false; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex; >+ if ((vectorIndex = (position / BitCacheSize) - 1) >= >+ this.extra[0].length) { >+ return false; // if not enough room in vector, then not initialized >+ } >+ return (~this.extra[4][vectorIndex] & >+ this.extra[5][vectorIndex] & >+ (1L << (position % BitCacheSize))) != 0; >+} >+ >+public void markAsComparedEqualToNonNull(LocalVariableBinding local) { >+ // protected from non-object locals in calling methods >+ if (this != DEAD_END) { >+ this.tagBits |= NULL_FLAG_MASK; >+ int position; >+ long mask; >+ // position is zero-based >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { > // use bits >- return (potentialInits & (1L << position)) != 0; >+ if (((mask = 1L << position) & // leave assigned non null unchanged >+ this.nullAssignmentStatusBit1 & >+ ~this.nullAssignmentStatusBit2 & >+ ~this.nullAssignmentValueBit1 & >+ this.nullAssignmentValueBit2) == 0) { >+ // set protected non null >+ this.nullAssignmentStatusBit1 |= mask; >+ this.nullAssignmentStatusBit2 |= mask; >+ // clear potential null >+ this.nullAssignmentValueBit1 &= ~mask; >+ } >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ if (this.extra == null) { >+ int length = vectorIndex + 1; >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[length]; >+ } >+ } >+ else { >+ int oldLength; >+ if (vectorIndex >= (oldLength = this.extra[0].length)) { >+ int newLength = vectorIndex + 1; >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[newLength]), 0, >+ oldLength); >+ } >+ } >+ } >+ if (((mask = 1L << (position % BitCacheSize)) & >+ this.extra[2][vectorIndex] & >+ ~this.extra[3][vectorIndex] & >+ ~this.extra[4][vectorIndex] & >+ this.extra[5][vectorIndex]) == 0) { >+ this.extra[2][vectorIndex] |= mask; >+ this.extra[3][vectorIndex] |= mask; >+ this.extra[4][vectorIndex] &= ~mask; >+ } > } >- // use extra vector >- if (extraPotentialInits == null) >- return false; // if vector not yet allocated, then not initialized >- int vectorIndex; >- if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length) >- return false; // if not enough room in vector, then not initialized >- return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0; >- } >- >- /** >- * Check status of definite assignment for a field. >- */ >- final public boolean isPotentiallyAssigned(FieldBinding field) { >- >- return isPotentiallyAssigned(field.id); > } >- >- /** >- * Check status of potential assignment for a local. >- */ >- final public boolean isPotentiallyAssigned(LocalVariableBinding local) { >- >- // final constants are inlined, and thus considered as always initialized >- if (local.constant() != Constant.NotAConstant) { >- return true; >+} >+ >+public void markAsComparedEqualToNull(LocalVariableBinding local) { >+ // protected from non-object locals in calling methods >+ if (this != DEAD_END) { >+ this.tagBits |= NULL_FLAG_MASK; >+ int position; >+ long mask, unknownAssigned; >+ // position is zero-based >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { >+ // use bits >+ mask = 1L << position; >+ if ((mask & // leave assigned null unchanged >+ this.nullAssignmentStatusBit1 & >+ ~this.nullAssignmentStatusBit2 & >+ this.nullAssignmentValueBit1 & >+ ~this.nullAssignmentValueBit2) == 0) { >+ unknownAssigned = this.nullAssignmentStatusBit1 & >+ ~this.nullAssignmentStatusBit2 & >+ this.nullAssignmentValueBit1 & >+ this.nullAssignmentValueBit2; >+ // set protected >+ this.nullAssignmentStatusBit2 |= mask; >+ this.nullAssignmentStatusBit1 &= (mask = ~mask); >+ // protected is null >+ this.nullAssignmentValueBit1 &= mask | ~unknownAssigned; >+ this.nullAssignmentValueBit2 &= mask; >+ // clear potential anything but null >+ } >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ mask = 1L << (position % BitCacheSize); >+ if (this.extra == null) { >+ int length = vectorIndex + 1; >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[length ]; >+ } >+ } >+ else { >+ int oldLength; >+ if (vectorIndex >= (oldLength = this.extra[0].length)) { >+ int newLength = vectorIndex + 1; >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[newLength]), 0, >+ oldLength); >+ } >+ } >+ } >+ if ((mask & >+ this.extra[2][vectorIndex] & >+ ~this.extra[3][vectorIndex] & >+ this.extra[4][vectorIndex] & >+ ~this.extra[5][vectorIndex]) == 0) { >+ unknownAssigned = this.extra[2][vectorIndex] & >+ ~this.extra[3][vectorIndex] & >+ this.extra[4][vectorIndex] & >+ this.extra[5][vectorIndex]; >+ this.extra[3][vectorIndex] |= mask; >+ this.extra[2][vectorIndex] &= (mask = ~mask); >+ this.extra[4][vectorIndex] &= mask | ~unknownAssigned; >+ this.extra[5][vectorIndex] &= mask; >+ } > } >- return isPotentiallyAssigned(local.id + maxFieldCount); > } >+} >+ >+/** >+ * Record a definite assignment at a given position. >+ * It deals with the dual representation of the InitializationInfo2: >+ * bits for the first 64 entries, then an array of booleans. >+ */ >+final private void markAsDefinitelyAssigned(int position) { > >- /** >- * Record a definite assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private void markAsDefinitelyAssigned(int position) { >- >- if (this != DEAD_END) { >- >- // position is zero-based >- if (position < BitCacheSize) { >- // use bits >- long mask; >- definiteInits |= (mask = 1L << position); >- potentialInits |= mask; >- definiteNulls &= ~mask; >- definiteNonNulls &= ~mask; >- } else { >- // use extra vector >- int vectorIndex = (position / BitCacheSize) - 1; >- if (extraDefiniteInits == null) { >- int length; >- extraDefiniteInits = new long[length = vectorIndex + 1]; >- extraPotentialInits = new long[length]; >- extraDefiniteNulls = new long[length]; >- extraDefiniteNonNulls = new long[length]; >- } else { >- int oldLength; // might need to grow the arrays >- if (vectorIndex >= (oldLength = extraDefiniteInits.length)) { >- System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength); >- System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength); >- System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[vectorIndex + 1]), 0, oldLength); >- System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[vectorIndex + 1]), 0, oldLength); >+ if (this != DEAD_END) { >+ // position is zero-based >+ if (position < BitCacheSize) { >+ // use bits >+ long mask; >+ this.definiteInits |= (mask = 1L << position); >+ this.potentialInits |= mask; >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ if (this.extra == null) { >+ int length = vectorIndex + 1; >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[length]; >+ } >+ } >+ else { >+ int oldLength; // might need to grow the arrays >+ if (vectorIndex >= (oldLength = this.extra[0].length)) { >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[vectorIndex + 1]), 0, >+ oldLength); > } > } >- long mask; >- extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); >- extraPotentialInits[vectorIndex] |= mask; >- extraDefiniteNulls[vectorIndex] &= ~mask; >- extraDefiniteNonNulls[vectorIndex] &= ~mask; > } >+ long mask; >+ this.extra[0][vectorIndex] |= >+ (mask = 1L << (position % BitCacheSize)); >+ this.extra[1][vectorIndex] |= mask; > } > } >- >- /** >- * Record a field got definitely assigned. >- */ >- public void markAsDefinitelyAssigned(FieldBinding field) { >- if (this != DEAD_END) >- markAsDefinitelyAssigned(field.id); >+} >+ >+public void markAsDefinitelyAssigned(FieldBinding field) { >+ if (this != DEAD_END) >+ markAsDefinitelyAssigned(field.id); >+} >+ >+public void markAsDefinitelyAssigned(LocalVariableBinding local) { >+ if (this != DEAD_END) >+ markAsDefinitelyAssigned(local.id + this.maxFieldCount); >+} >+ >+/** >+ * Record a definite non-null assignment at a given position. >+ */ >+final private void markAsDefinitelyNonNull(int position) { >+ // DEAD_END guarded above >+ this.tagBits |= NULL_FLAG_MASK; >+ long mask; >+ // position is zero-based >+ if (position < BitCacheSize) { >+ // use bits >+ this.nullAssignmentStatusBit1 |= (mask = 1L << position); >+ this.nullAssignmentValueBit2 |= mask; // set non null >+ this.nullAssignmentStatusBit2 &= ~mask; // clear protection >+ this.nullAssignmentValueBit1 &= ~mask; // clear null >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ this.extra[2][vectorIndex] |= >+ (mask = 1L << (position % BitCacheSize)); >+ this.extra[5][vectorIndex] |= mask; >+ this.extra[3][vectorIndex] &= ~mask; >+ this.extra[4][vectorIndex] &= ~mask; > } >- >- /** >- * Record a local got definitely assigned. >- */ >- public void markAsDefinitelyAssigned(LocalVariableBinding local) { >- if (this != DEAD_END) >- markAsDefinitelyAssigned(local.id + maxFieldCount); >- } >- >- /** >- * Record a definite non-null assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private void markAsDefinitelyNonNull(int position) { >- >- if (this != DEAD_END) { >- >- // position is zero-based >- if (position < BitCacheSize) { >- // use bits >- long mask; >- definiteNonNulls |= (mask = 1L << position); >- definiteNulls &= ~mask; >- } else { >- // use extra vector >- int vectorIndex = (position / BitCacheSize) - 1; >- long mask; >- extraDefiniteNonNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); >- extraDefiniteNulls[vectorIndex] &= ~mask; >- } >- } >+} >+ >+public void markAsDefinitelyNonNull(FieldBinding field) { >+ if (this != DEAD_END) { >+ markAsDefinitelyNonNull(field.id); > } >+} > >- /** >- * Record a field got definitely assigned to non-null value. >- */ >- public void markAsDefinitelyNonNull(FieldBinding field) { >- if (this != DEAD_END) >- markAsDefinitelyNonNull(field.id); >+public void markAsDefinitelyNonNull(LocalVariableBinding local) { >+ // protected from non-object locals in calling methods >+ if (this != DEAD_END) { >+ markAsDefinitelyNonNull(local.id + this.maxFieldCount); > } >- >- /** >- * Record a local got definitely assigned to non-null value. >- */ >- public void markAsDefinitelyNonNull(LocalVariableBinding local) { >- if (this != DEAD_END) >- markAsDefinitelyNonNull(local.id + maxFieldCount); >- } >- >- /** >- * Record a definite null assignment at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private void markAsDefinitelyNull(int position) { >- >- if (this != DEAD_END) { >- >- // position is zero-based >- if (position < BitCacheSize) { >- // use bits >- long mask; >- definiteNulls |= (mask = 1L << position); >- definiteNonNulls &= ~mask; >- } else { >- // use extra vector >- int vectorIndex = (position / BitCacheSize) - 1; >- long mask; >- extraDefiniteNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize)); >- extraDefiniteNonNulls[vectorIndex] &= ~mask; >- } >- } >+} >+ >+/** >+ * Record a definite null assignment at a given position. >+ */ >+final private void markAsDefinitelyNull(int position) { >+ // DEAD_END guarded above >+ this.tagBits |= NULL_FLAG_MASK; >+ long mask; >+ if (position < BitCacheSize) { >+ // use bits >+ this.nullAssignmentStatusBit1 |= (mask = 1L << position); // set assignment >+ this.nullAssignmentStatusBit2 &= ~mask; // clear protection >+ this.nullAssignmentValueBit1 |= mask; // set null >+ this.nullAssignmentValueBit2 &= ~mask; // clear non null >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ this.extra[2][vectorIndex] |= >+ (mask = 1L << (position % BitCacheSize)); >+ this.extra[3][vectorIndex] &= ~mask; >+ this.extra[4][vectorIndex] |= mask; >+ this.extra[5][vectorIndex] &= ~mask; > } >+} > >- /** >- * Record a field got definitely assigned to null. >- */ >- public void markAsDefinitelyNull(FieldBinding field) { >- if (this != DEAD_END) >- markAsDefinitelyAssigned(field.id); >+public void markAsDefinitelyNull(FieldBinding field) { >+ if (this != DEAD_END) { >+ markAsDefinitelyNull(field.id); > } >- >- /** >- * Record a local got definitely assigned to null. >- */ >- public void markAsDefinitelyNull(LocalVariableBinding local) { >- if (this != DEAD_END) >- markAsDefinitelyNull(local.id + maxFieldCount); >+} >+ >+public void markAsDefinitelyNull(LocalVariableBinding local) { >+ // protected from non-object locals in calling methods >+ if (this != DEAD_END) { >+ markAsDefinitelyNull(local.id + this.maxFieldCount); > } >- >- /** >- * Clear initialization information at a given position. >- * It deals with the dual representation of the InitializationInfo2: >- * bits for the first 64 entries, then an array of booleans. >- */ >- final private void markAsDefinitelyNotAssigned(int position) { >- if (this != DEAD_END) { >- >- // position is zero-based >- if (position < BitCacheSize) { >- // use bits >- long mask; >- definiteInits &= ~(mask = 1L << position); >- potentialInits &= ~mask; >- definiteNulls &= ~mask; >- definiteNonNulls &= ~mask; >- } else { >- // use extra vector >- int vectorIndex = (position / BitCacheSize) - 1; >- if (extraDefiniteInits == null) { >- return; // nothing to do, it was not yet set >- } >- // might need to grow the arrays >- if (vectorIndex >= extraDefiniteInits.length) { >- return; // nothing to do, it was not yet set >- } >- long mask; >- extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize)); >- extraPotentialInits[vectorIndex] &= ~mask; >- extraDefiniteNulls[vectorIndex] &= ~mask; >- extraDefiniteNonNulls[vectorIndex] &= ~mask; >- } >+} >+ >+/** >+ * Mark a local as having been assigned to an unknown value. >+ * @param local the local to mark >+ */ >+// PREMATURE may try to get closer to markAsDefinitelyAssigned, but not >+// obvious >+public void markAsDefinitelyUnknown(LocalVariableBinding local) { >+ // protected from non-object locals in calling methods >+ if (this != DEAD_END) { >+ this.tagBits |= NULL_FLAG_MASK; >+ long mask; >+ int position; >+ // position is zero-based >+ if ((position = local.id + this.maxFieldCount) < BitCacheSize) { >+ // use bits >+ this.nullAssignmentValueBit1 |= (mask = 1L << position); >+ this.nullAssignmentValueBit2 |= mask; >+ // set unknown >+ this.nullAssignmentStatusBit1 |= mask; >+ // set assignment >+ this.nullAssignmentStatusBit2 &= ~mask; >+ // clear protection >+ } >+ else { >+ // use extra vector >+ int vectorIndex = (position / BitCacheSize) - 1; >+ this.extra[4][vectorIndex] |= >+ (mask = 1L << (position % BitCacheSize)); >+ this.extra[5][vectorIndex] |= mask; >+ this.extra[2][vectorIndex] |= mask; >+ this.extra[3][vectorIndex] &= ~mask; > } > } >- >- /** >- * Clear the initialization info for a field >- */ >- public void markAsDefinitelyNotAssigned(FieldBinding field) { >- >- if (this != DEAD_END) >- markAsDefinitelyNotAssigned(field.id); >+} >+ >+public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { >+ if ((otherInits.tagBits & UNREACHABLE) != 0 && this != DEAD_END) { >+ // DEAD_END + unreachable other -> other >+ return this; > } >- >- /** >- * Clear the initialization info for a local variable >- */ >- >- public void markAsDefinitelyNotAssigned(LocalVariableBinding local) { >- >- if (this != DEAD_END) >- markAsDefinitelyNotAssigned(local.id + maxFieldCount); >- } >- >- /** >- * Returns the receiver updated in the following way: <ul> >- * <li> intersection of definitely assigned variables, >- * <li> union of potentially assigned variables. >- * </ul> >- */ >- public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { >- >- if (this == DEAD_END) return otherInits; >- if (otherInits == DEAD_END) return this; >- >- if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){ >- if ((this.reachMode & UNREACHABLE) != 0){ >- return otherInits; >- } >- return this; >- } >- >- // if one branch is not fake reachable, then the merged one is reachable >- this.reachMode &= otherInits.reachMode; >- >- // intersection of definitely assigned variables, >- this.definiteInits &= otherInits.definiteInits; >- // union of potentially set ones >- this.potentialInits |= otherInits.potentialInits; >- // intersection of definitely null variables, >- this.definiteNulls &= otherInits.definiteNulls; >- // intersection of definitely non-null variables, >- this.definiteNonNulls &= otherInits.definiteNonNulls; >- >- // treating extra storage >- if (this.extraDefiniteInits != null) { >- if (otherInits.extraDefiniteInits != null) { >+ if ((this.tagBits & UNREACHABLE) != 0) { >+ return (UnconditionalFlowInfo) otherInits.copy(); // make sure otherInits won't be affected >+ } >+ >+ // intersection of definitely assigned variables, >+ this.definiteInits &= otherInits.definiteInits; >+ // union of potentially set ones >+ this.potentialInits |= otherInits.potentialInits; >+ >+ // null combinations >+ boolean otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0, >+ thisHasNulls = false; >+ long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4; >+ if (otherHasNulls) { >+ this.nullAssignmentStatusBit1 = >+ (a1 = this.nullAssignmentStatusBit1) & >+ (b1 = otherInits.nullAssignmentStatusBit1) & ( >+ (nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) & >+ ((b2 = otherInits.nullAssignmentStatusBit2) & >+ (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & >+ (na3 = ~(a3 = this.nullAssignmentValueBit1)) & >+ ((a2 = this.nullAssignmentStatusBit2) & >+ (na4 = ~(a4 = this.nullAssignmentValueBit2)) | a4) | >+ (na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) | >+ b4 & (na3 & nb3 & (na4 & a2 | a4) | >+ na2 & a4 & nb2)); >+ this.nullAssignmentStatusBit2 = >+ a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) | >+ a1 & b1 & (a2 ^ b2) & na3 & nb3 | >+ (a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4; >+ this.nullAssignmentValueBit1 = >+ b1 & nb2 & nb4 | >+ ~a1 & (a3 | >+ a2 & na3 & (b1 | nb2)) | >+ (a1 | na2) & nb1 & b2 & nb3 | >+ nb1 & b3 | >+ a1 & na2 & (na4 | >+ b1 & nb2 & (a3 | b3)); >+ this.nullAssignmentValueBit2 = >+ a4 | b4; >+ } >+ else { >+ // tune potentials >+ this.nullAssignmentValueBit1 = >+ ~(~this.nullAssignmentStatusBit1 & >+ ~this.nullAssignmentStatusBit2 & >+ ~this.nullAssignmentValueBit1) & >+ ~(this.nullAssignmentStatusBit1 & >+ (this.nullAssignmentStatusBit2 | this.nullAssignmentValueBit2)); >+ // reset assignment and protected >+ this.nullAssignmentStatusBit1 = >+ this.nullAssignmentStatusBit2 = 0; >+ } >+ thisHasNulls = this.nullAssignmentStatusBit1 != 0 || >+ this.nullAssignmentStatusBit2 != 0 || >+ this.nullAssignmentValueBit1 != 0 || >+ this.nullAssignmentValueBit2 != 0; >+ >+ // treating extra storage >+ if (this.extra != null || otherInits.extra != null) { >+ int mergeLimit = 0, copyLimit = 0, resetLimit = 0; >+ if (this.extra != null) { >+ if (otherInits.extra != null) { > // both sides have extra storage >- int i = 0, length, otherLength; >- if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) { >- // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?) >- System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length); >- System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length); >- System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length); >- System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length); >- while (i < length) { >- this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i]; >- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; >- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; >+ int length, otherLength; >+ if ((length = this.extra[0].length) < >+ (otherLength = otherInits.extra[0].length)) { >+ // current storage is shorter -> grow current >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], 0, >+ (this.extra[j] = new long[otherLength]), 0, length); > } >- while (i < otherLength) { >- this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++]; >- } >- } else { >+ mergeLimit = length; >+ copyLimit = otherLength; >+ } >+ else { > // current storage is longer >- while (i < otherLength) { >- this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i]; >- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i]; >- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i]; >- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++]; >- } >- while (i < length) { >- this.extraDefiniteInits[i] = 0; >- this.extraDefiniteNulls[i] = 0; >- this.extraDefiniteNonNulls[i++] = 0; >- } >+ mergeLimit = otherLength; >+ resetLimit = length; > } >- } else { >- // no extra storage on otherInits >- int i = 0, length = this.extraDefiniteInits.length; >- while (i < length) { >- this.extraDefiniteInits[i] = 0; >- this.extraDefiniteNulls[i] = 0; >- this.extraDefiniteNonNulls[i++] = 0; >- } >- } >- } else >- if (otherInits.extraDefiniteInits != null) { >- // no storage here, but other has extra storage. >- int otherLength; >- this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]; >- System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength); >- this.extraDefiniteNulls = new long[otherLength]; >- this.extraDefiniteNonNulls = new long[otherLength]; >+ } >+ else { >+ resetLimit = this.extra[0].length; > } >- return this; >+ } >+ else if (otherInits.extra != null) { >+ // no storage here, but other has extra storage. >+ int otherLength = otherInits.extra[0].length; >+ this.extra = new long[extraLength][]; >+ for (int j = 0; j < extraLength; j++) { >+ this.extra[j] = new long[otherLength]; >+ } >+ System.arraycopy(otherInits.extra[1], 0, >+ this.extra[1], 0, otherLength); >+ copyLimit = otherLength; >+ } >+ int i; >+ if (otherHasNulls) { >+ for (i = 0; i < mergeLimit; i++) { >+ this.extra[2][i] = >+ (a1 = this.extra[2][i]) & >+ (b1 = otherInits.extra[2][i]) & ( >+ (nb4 = ~(b4 = otherInits.extra[5][i])) & >+ ((b2 = otherInits.extra[3][i]) & >+ (nb3 = ~(b3 = otherInits.extra[4][i])) & >+ (na3 = ~(a3 = this.extra[4][i])) & >+ ((a2 = this.extra[3][i]) & >+ (na4 = ~(a4 = this.extra[5][i])) | a4) | >+ (na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) | >+ b4 & (na3 & nb3 & (na4 & a2 | a4) | >+ na2 & a4 & nb2)); >+ this.extra[3][i] = >+ a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) | >+ a1 & b1 & (a2 ^ b2) & na3 & nb3 | >+ (a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4; >+ this.extra[4][i] = >+ b1 & nb2 & nb4 | >+ ~a1 & (a3 | >+ a2 & na3 & (b1 | nb2)) | >+ (a1 | na2) & nb1 & b2 & nb3 | >+ nb1 & b3 | >+ a1 & na2 & (na4 | >+ b1 & nb2 & (a3 | b3)); >+ this.extra[5][i] = >+ a4 | b4; >+ thisHasNulls = thisHasNulls || >+ this.extra[5][i] != 0 || >+ this.extra[2][i] != 0 || >+ this.extra[3][i] != 0 || >+ this.extra[4][i] != 0; >+ } >+ } >+ else { >+ for (i = 0; i < mergeLimit; i++) { >+ this.extra[0][i] &= >+ otherInits.extra[0][i]; >+ this.extra[1][i] |= >+ otherInits.extra[1][i]; >+ this.extra[4][i] = >+ ~(~this.extra[2][i] & >+ ~this.extra[3][i] & >+ ~this.extra[4][i]) & >+ ~(this.extra[2][i] & >+ (this.extra[3][i] | >+ this.extra[5][i])); >+ this.extra[2][i] = >+ this.extra[3][i] = 0; >+ thisHasNulls = thisHasNulls || >+ this.extra[4][i] != 0 || >+ this.extra[5][i] != 0; >+ } >+ } >+ for (; i < copyLimit; i++) { >+ this.extra[1][i] = otherInits.extra[1][i]; >+ this.extra[4][i] = >+ ~(~otherInits.extra[2][i] & >+ ~otherInits.extra[3][i] & >+ ~otherInits.extra[4][i]) & >+ ~(otherInits.extra[2][i] & >+ (otherInits.extra[3][i] | >+ otherInits.extra[5][i])); >+ this.extra[5][i] = otherInits.extra[5][i]; >+ thisHasNulls = thisHasNulls || >+ this.extra[4][i] != 0 || >+ this.extra[5][i] != 0; >+ } >+ for (; i < resetLimit; i++) { >+ this.extra[4][i] = >+ ~(~this.extra[2][i] & >+ ~this.extra[3][i] & >+ ~this.extra[4][i]) & >+ ~(this.extra[2][i] & >+ (this.extra[3][i] | >+ this.extra[5][i])); >+ this.extra[0][i] = >+ this.extra[2][i] = >+ this.extra[3][i] = 0; >+ thisHasNulls = thisHasNulls || >+ this.extra[4][i] != 0 || >+ this.extra[5][i] != 0; >+ } > } >- >- /* >- * Answer the total number of fields in enclosing types of a given type >- */ >- static int numberOfEnclosingFields(ReferenceBinding type){ >- >- int count = 0; >+ if (thisHasNulls) { >+ this.tagBits |= NULL_FLAG_MASK; >+ } >+ else { >+ this.tagBits &= ~NULL_FLAG_MASK; >+ } >+ return this; >+} >+ >+/* >+ * Answer the total number of fields in enclosing types of a given type >+ */ >+static int numberOfEnclosingFields(ReferenceBinding type){ >+ int count = 0; >+ type = type.enclosingType(); >+ while(type != null) { >+ count += type.fieldCount(); > type = type.enclosingType(); >- while(type != null) { >- count += type.fieldCount(); >- type = type.enclosingType(); >- } >- return count; > } >- >- public int reachMode(){ >- return this.reachMode; >+ return count; >+} >+ >+public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { >+ if (this == DEAD_END) { >+ return this; > } >- >- public FlowInfo setReachMode(int reachMode) { >- >- if (this == DEAD_END) return this; // cannot modify DEAD_END >- >- // reset optional inits when becoming unreachable >- if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) { >+ UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); >+ copy.definiteInits = this.definiteInits; >+ copy.potentialInits = this.potentialInits; >+ copy.tagBits = this.tagBits & ~NULL_FLAG_MASK; >+ copy.maxFieldCount = this.maxFieldCount; >+ if (this.extra != null) { >+ int length; >+ copy.extra = new long[extraLength][]; >+ System.arraycopy(this.extra[0], 0, >+ (copy.extra[0] = >+ new long[length = this.extra[0].length]), 0, length); >+ System.arraycopy(this.extra[1], 0, >+ (copy.extra[1] = new long[length]), 0, length); >+ for (int j = 2; j < extraLength; j++) { >+ copy.extra[j] = new long[length]; >+ } >+ } >+ return copy; >+} >+ >+public FlowInfo safeInitsWhenTrue() { >+ return copy(); >+} >+ >+public FlowInfo setReachMode(int reachMode) { >+ if (reachMode == REACHABLE && this != DEAD_END) { // cannot modify DEAD_END >+ this.tagBits &= ~UNREACHABLE; >+ } >+ else { >+ if ((this.tagBits & UNREACHABLE) == 0) { >+ // reset optional inits when becoming unreachable >+ // see InitializationTest#test090 (and others) > this.potentialInits = 0; >- if (this.extraPotentialInits != null){ >- for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){ >- this.extraPotentialInits[i] = 0; >+ if (this.extra != null) { >+ for (int i = 0, length = this.extra[0].length; >+ i < length; i++) { >+ this.extra[1][i] = 0; > } > } > } >- this.reachMode = reachMode; >- >- return this; >+ this.tagBits |= UNREACHABLE; > } >+ return this; >+} > >- public String toString(){ >- >- if (this == DEAD_END){ >- return "FlowInfo.DEAD_END"; //$NON-NLS-1$ >- } >- return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$ >- +", pot: " + this.potentialInits //$NON-NLS-1$ >- + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$ >- +", defNull: " + this.definiteNulls //$NON-NLS-1$ >- +", defNonNull: " + this.definiteNonNulls //$NON-NLS-1$ >- +">"; //$NON-NLS-1$ >+public String toString(){ >+ // PREMATURE consider printing bit fields as 0001 0001 1000 0001... >+ if (this == DEAD_END){ >+ return "FlowInfo.DEAD_END"; //$NON-NLS-1$ >+ } >+ if ((this.tagBits & NULL_FLAG_MASK) != 0) { >+ if (this.extra == null) { >+ return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$ >+ +", pot: " + this.potentialInits //$NON-NLS-1$ >+ + ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ +", nullS1: " + this.nullAssignmentStatusBit1 //$NON-NLS-1$ >+ +", nullS2: " + this.nullAssignmentStatusBit2 //$NON-NLS-1$ >+ +", nullV1: " + this.nullAssignmentValueBit1 //$NON-NLS-1$ >+ +", nullV2: " + this.nullAssignmentValueBit2 //$NON-NLS-1$ >+ +">"; //$NON-NLS-1$ >+ } >+ else { >+ String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$ >+ pot = "], pot:[" + this.potentialInits, //$NON-NLS-1$ >+ nullS1 = ", nullS1:[" + this.nullAssignmentStatusBit1, //$NON-NLS-1$ >+ nullS2 = "], nullS2:[" + this.nullAssignmentStatusBit2, //$NON-NLS-1$ >+ nullV1 = "], nullV1:[" + this.nullAssignmentValueBit1, //$NON-NLS-1$ >+ nullV2 = "], nullV2:[" + this.nullAssignmentValueBit2; //$NON-NLS-1$ >+ int i, ceil; >+ for (i = 0, ceil = this.extra[0].length > 3 ? >+ 3 : >+ this.extra[0].length; >+ i < ceil; i++) { >+ def += "," + this.extra[0][i]; //$NON-NLS-1$ >+ pot += "," + this.extra[1][i]; //$NON-NLS-1$ >+ nullS1 += "," + this.extra[2][i]; //$NON-NLS-1$ >+ nullS2 += "," + this.extra[3][i]; //$NON-NLS-1$ >+ nullV1 += "," + this.extra[4][i]; //$NON-NLS-1$ >+ nullV2 += "," + this.extra[5][i]; //$NON-NLS-1$ >+ } >+ if (ceil < this.extra[0].length) { >+ def += ",..."; //$NON-NLS-1$ >+ pot += ",..."; //$NON-NLS-1$ >+ nullS1 += ",..."; //$NON-NLS-1$ >+ nullS2 += ",..."; //$NON-NLS-1$ >+ nullV1 += ",..."; //$NON-NLS-1$ >+ nullV2 += ",..."; //$NON-NLS-1$ >+ } >+ return def + pot >+ + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ + nullS1 + nullS2 + nullV1 + nullV2 >+ + "]>"; //$NON-NLS-1$ >+ } >+ } >+ else { >+ if (this.extra == null) { >+ return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$ >+ +", pot: " + this.potentialInits //$NON-NLS-1$ >+ + ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ +", no null info>"; //$NON-NLS-1$ >+ } >+ else { >+ String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$ >+ pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$ >+ int i, ceil; >+ for (i = 0, ceil = this.extra[0].length > 3 ? >+ 3 : >+ this.extra[0].length; >+ i < ceil; i++) { >+ def += "," + this.extra[0][i]; //$NON-NLS-1$ >+ pot += "," + this.extra[1][i]; //$NON-NLS-1$ >+ } >+ if (ceil < this.extra[0].length) { >+ def += ",..."; //$NON-NLS-1$ >+ pot += ",..."; //$NON-NLS-1$ >+ } >+ return def + pot >+ + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$ >+ + ", no null info>"; //$NON-NLS-1$ >+ } > } >+} >+ >+public UnconditionalFlowInfo unconditionalCopy() { >+ return (UnconditionalFlowInfo) copy(); >+} > >- public UnconditionalFlowInfo unconditionalInits() { >- >- // also see conditional inits, where it requests them to merge >- return this; >+public UnconditionalFlowInfo unconditionalFieldLessCopy() { >+ // TODO (maxime) may consider leveraging null contribution verification as it is done in copy >+ UnconditionalFlowInfo copy = new UnconditionalFlowInfo(); >+ copy.tagBits = this.tagBits; >+ copy.maxFieldCount = this.maxFieldCount; >+ int limit = this.maxFieldCount; >+ if (limit < BitCacheSize) { >+ long mask; >+ copy.definiteInits = this.definiteInits & (mask = ~((1L << limit)-1)); >+ copy.potentialInits = this.potentialInits & mask; >+ copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1 & mask; >+ copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2 & mask; >+ copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1 & mask; >+ copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2 & mask; >+ } >+ // use extra vector >+ if (this.extra == null) { >+ return copy; // if vector not yet allocated, then not initialized >+ } >+ int vectorIndex, length, copyStart; >+ if ((vectorIndex = (limit / BitCacheSize) - 1) >= >+ (length = this.extra[0].length)) { >+ return copy; // not enough room yet >+ } >+ long mask; >+ copy.extra = new long[extraLength][]; >+ if ((copyStart = vectorIndex + 1) < length) { >+ int copyLength = length - copyStart; >+ for (int j = 0; j < extraLength; j++) { >+ System.arraycopy(this.extra[j], copyStart, >+ (copy.extra[j] = new long[length]), copyStart, >+ copyLength); >+ } >+ } >+ else if (vectorIndex >= 0) { >+ for (int j = 0; j < extraLength; j++) { >+ copy.extra[j] = new long[length]; >+ } >+ } >+ if (vectorIndex >= 0) { >+ mask = ~((1L << (limit % BitCacheSize))-1); >+ for (int j = 0; j < extraLength; j++) { >+ copy.extra[j][vectorIndex] = >+ this.extra[j][vectorIndex] & mask; >+ } > } >+ return copy; >+} >+ >+public UnconditionalFlowInfo unconditionalInits() { >+ // also see conditional inits, where it requests them to merge >+ return this; >+} >+ >+public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { >+ return this; >+} > } >Index: compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java,v >retrieving revision 1.150 >diff -u -r1.150 CompilerOptions.java >--- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 14 Nov 2005 18:47:41 -0000 1.150 >+++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 28 Nov 2005 12:08:08 -0000 >@@ -984,6 +984,8 @@ > case 'n' : > if ("nls".equals(warningToken)) //$NON-NLS-1$ > return NonExternalizedString; >+ if ("null".equals(warningToken)) //$NON-NLS-1$ >+ return NullReference; > break; > case 's' : > if ("serial".equals(warningToken)) //$NON-NLS-1$ >Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java,v >retrieving revision 1.56 >diff -u -r1.56 MethodScope.java >--- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 20 Oct 2005 13:26:45 -0000 1.56 >+++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java 28 Nov 2005 12:08:08 -0000 >@@ -402,8 +402,9 @@ > > if (!flowInfo.isReachable()) return -1; > >- UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits(); >- long[] extraInits = unconditionalFlowInfo.extraDefiniteInits; >+ UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); >+ long[] extraInits = unconditionalFlowInfo.extra == null ? >+ null : unconditionalFlowInfo.extra[0]; > long inits = unconditionalFlowInfo.definiteInits; > checkNextEntry : for (int i = lastIndex; --i >= 0;) { > if (definiteInits[i] == inits) { >Index: compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java,v >retrieving revision 1.274 >diff -u -r1.274 ProblemReporter.java >--- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 16 Nov 2005 12:19:37 -0000 1.274 >+++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 28 Nov 2005 12:08:10 -0000 >@@ -1374,6 +1374,7 @@ > > case IProblem.LocalVariableCannotBeNull : > case IProblem.LocalVariableCanOnlyBeNull : >+ case IProblem.LocalVariableMayBeNull : > return CompilerOptions.NullReference; > > case IProblem.BoxingConversion : >@@ -3980,6 +3981,15 @@ > local.sourceEnd); > } > } >+public void localVariableMayBeNull(LocalVariableBinding local, ASTNode location) { >+ String[] arguments = new String[] {new String(local.name)}; >+ this.handle( >+ IProblem.LocalVariableMayBeNull, >+ arguments, >+ arguments, >+ location.sourceStart, >+ location.sourceEnd); >+} > public void methodMustOverride(AbstractMethodDeclaration method) { > MethodBinding binding = method.binding; > this.handle( >Index: compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties >=================================================================== >RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties,v >retrieving revision 1.190 >diff -u -r1.190 messages.properties >--- compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 21 Oct 2005 07:19:17 -0000 1.190 >+++ compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties 28 Nov 2005 12:08:10 -0000 >@@ -324,6 +324,7 @@ > 396 = Illegal modifier for the variable {0}; only final is permitted > 397 = The variable {0} cannot be null; it was either set to a non-null value or assumed to be non-null when last used > 398 = The variable {0} can only be null; it was either set to null or checked for null when last used >+399 = The variable {0} may be null > > 400 = The type {3} must implement the inherited abstract method {2}.{0}({1}) > 401 = Cannot override the final method from {0}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 110030
:
30700
|
33507
|
33699