### Eclipse Workspace Patch 1.0
#P org.eclipse.jdt.core
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.164
diff -u -r1.164 IProblem.java
--- compiler/org/eclipse/jdt/core/compiler/IProblem.java 10 Jan 2006 21:01:07 -0000 1.164
+++ compiler/org/eclipse/jdt/core/compiler/IProblem.java 24 Jan 2006 09:56:37 -0000
@@ -688,6 +688,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 24 Jan 2006 09:56:37 -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/ASTNode.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java,v
retrieving revision 1.61
diff -u -r1.61 ASTNode.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java 19 Jan 2006 13:58:39 -0000 1.61
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java 24 Jan 2006 09:56:37 -0000
@@ -39,7 +39,7 @@
public final static int Bit15 = 0x4000; // is unnecessary cast (expression) | is varargs (type ref)
public final static int Bit16 = 0x8000; // in javadoc comment (name ref, type ref, msg)
public final static int Bit17 = 0x10000; // compound assigned (reference lhs)
- public final static int Bit18 = 0x20000;
+ public final static int Bit18 = 0x20000; // non null (expression)
public final static int Bit19 = 0x40000;
public final static int Bit20 = 0x80000;
public final static int Bit21 = 0x100000;
@@ -158,6 +158,9 @@
// for array initializer
public static final int IsAnnotationDefaultValue = Bit1;
+ // for null refenrence analysis
+ public static final int IsNonNull = Bit18;
+
public ASTNode() {
super();
Index: compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java,v
retrieving revision 1.57
diff -u -r1.57 AllocationExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 10 Jan 2006 14:37:27 -0000 1.57
+++ compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java 24 Jan 2006 09:56:37 -0000
@@ -164,7 +164,7 @@
*/
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
// perform some emulation work in case there is some and we are inside a local type only
@@ -180,11 +180,12 @@
// request cascade of accesses
}
}
+ }
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// if constructor from parameterized type got found, use the original constructor at codegen time
this.codegenBinding = this.binding.original();
@@ -201,6 +202,7 @@
currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
}
}
+ }
}
public StringBuffer printExpression(int indent, StringBuffer output) {
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.39
diff -u -r1.39 ArrayReference.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 10 Jan 2006 14:37:27 -0000 1.39
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java 24 Jan 2006 09:56:37 -0000
@@ -27,34 +27,32 @@
sourceStart = rec.sourceStart;
}
- public FlowInfo analyseAssignment(
+public FlowInfo analyseAssignment(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo,
Assignment assignment,
boolean compoundAssignment) {
+ // TODO (maxime) optimization: unconditionalInits is applied to all existing calls
+ 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.46
diff -u -r1.46 AssertStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 13 Jan 2006 16:37:16 -0000 1.46
+++ compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java 24 Jan 2006 09:56:37 -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);
}
}
@@ -163,7 +164,7 @@
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// need assertion flag: $assertionsDisabled on outer most source clas
// (in case of static member of interface, will use the outermost static member - bug 22334)
@@ -186,6 +187,7 @@
break;
}
}
+ }
}
public StringBuffer printStatement(int tab, StringBuffer output) {
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.67
diff -u -r1.67 Assignment.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 10 Jan 2006 14:37:27 -0000 1.67
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java 24 Jan 2006 09:56:37 -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.54
diff -u -r1.54 BinaryExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 10 Jan 2006 14:37:27 -0000 1.54
+++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 24 Jan 2006 09:56:38 -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/Clinit.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java,v
retrieving revision 1.40
diff -u -r1.40 Clinit.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java 10 Jan 2006 14:37:27 -0000 1.40
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Clinit.java 24 Jan 2006 09:56:38 -0000
@@ -45,7 +45,8 @@
FlowInfo.DEAD_END);
// check for missing returning path
- this.needFreeReturn = flowInfo.isReachable();
+ this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;
+
// check missing blank final field initializations
flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn);
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 24 Jan 2006 09:56:38 -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.77
diff -u -r1.77 ConditionalExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 13 Jan 2006 16:37:16 -0000 1.77
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java 24 Jan 2006 09:56:38 -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 =
@@ -271,6 +265,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/ConstructorDeclaration.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java,v
retrieving revision 1.77
diff -u -r1.77 ConstructorDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java 10 Jan 2006 14:37:27 -0000 1.77
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java 24 Jan 2006 09:56:38 -0000
@@ -112,7 +112,8 @@
}
}
// check for missing returning path
- this.needFreeReturn = flowInfo.isReachable();
+ this.needFreeReturn = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;
+
// check missing blank final field initializations
if ((constructorCall != null)
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 24 Jan 2006 09:56:38 -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.45
diff -u -r1.45 DoStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 13 Jan 2006 16:37:16 -0000 1.45
+++ compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java 24 Jan 2006 09:56:38 -0000
@@ -46,6 +46,7 @@
LoopingFlowContext loopingContext =
new LoopingFlowContext(
flowContext,
+ flowInfo,
this,
breakLabel,
continueLabel,
@@ -59,12 +60,19 @@
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()) {
+ if ((actionInfo.tagBits &
+ loopingContext.initsOnContinue.tagBits &
+ FlowInfo.UNREACHABLE) != 0) {
continueLabel = null;
}
}
@@ -75,22 +83,35 @@
*/
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.tagBits & FlowInfo.UNREACHABLE) == 0 ?
+ 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.59
diff -u -r1.59 EqualExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 10 Jan 2006 14:37:27 -0000 1.59
+++ compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java 24 Jan 2006 09:56:38 -0000
@@ -22,84 +22,99 @@
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();
}
- }
- return right.analyseCode(
- currentScope, flowContext,
- left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
+ }
+ else {
+ result = right.analyseCode(
+ currentScope, flowContext,
+ left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).
+ /* unneeded since we flatten it: asNegatedCondition(). */
+ unconditionalInits();
+ }
+ }
+ if (result instanceof UnconditionalFlowInfo &&
+ (result.tagBits & FlowInfo.UNREACHABLE) == 0) { // 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/ExplicitConstructorCall.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java,v
retrieving revision 1.50
diff -u -r1.50 ExplicitConstructorCall.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java 10 Jan 2006 14:37:27 -0000 1.50
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java 24 Jan 2006 09:56:38 -0000
@@ -178,7 +178,7 @@
void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
ReferenceBinding superTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// perform some emulation work in case there is some and we are inside a local type only
if (superTypeErasure.isNestedType()
&& currentScope.enclosingSourceType().isLocalType()) {
@@ -190,11 +190,12 @@
currentScope.propagateInnerEmulation(superTypeErasure, qualification != null);
}
}
+ }
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// if constructor from parameterized type got found, use the original constructor at codegen time
this.codegenBinding = this.binding.original();
@@ -210,6 +211,7 @@
currentScope.problemReporter().needToEmulateMethodAccess(this.codegenBinding, this);
}
}
+ }
}
public StringBuffer printStatement(int indent, StringBuffer output) {
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.92
diff -u -r1.92 Expression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 17 Jan 2006 18:44:23 -0000 1.92
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 24 Jan 2006 09:56:38 -0000
@@ -481,31 +481,32 @@
}
}
- public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
- // do nothing by default - see EqualExpression
- }
-
- 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;
- }
+/**
+ * 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)) {
+ if ((this.bits & IsNonNull) == 0) {
+ flowContext.recordUsingNullReference(scope, local, this,
+ FlowContext.MAY_NULL, flowInfo);
}
- return flowInfo;
+ flowInfo.markAsComparedEqualToNonNull(local);
+ // from thereon it is set
}
-
- public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
+}
+
+public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
if (match == castType) {
if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
return true;
@@ -795,9 +796,18 @@
return null;
}
+/**
+ * Mark this expression as being non null, per a NON-NULL or NN tag in the
+ * source code.
+ */
+public void markAsNonNull() {
+ this.bits |= IsNonNull;
+}
+
public int nullStatus(FlowInfo flowInfo) {
- if (this.constant != null && this.constant != Constant.NotAConstant)
+ if ((this.bits & IsNonNull) != 0 ||
+ this.constant != null && this.constant != Constant.NotAConstant)
return FlowInfo.NON_NULL; // constant expression cannot be null
LocalVariableBinding local = localVariableBinding();
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.98
diff -u -r1.98 FieldReference.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 10 Jan 2006 14:37:27 -0000 1.98
+++ compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java 24 Jan 2006 09:56:39 -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*/);
@@ -349,7 +351,8 @@
* No need to emulate access to protected fields since not implicitly accessed
*/
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return;
+
// if field from parameterized type got found, use the original field at codegen time
this.codegenBinding = this.binding.original();
@@ -415,6 +418,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.54
diff -u -r1.54 ForStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 13 Jan 2006 16:37:16 -0000 1.54
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java 24 Jan 2006 09:56:39 -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,65 @@
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()) {
+ if ((actionInfo.tagBits &
+ loopingContext.initsOnContinue.tagBits &
+ FlowInfo.UNREACHABLE) != 0) {
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.29
diff -u -r1.29 ForeachStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 17 Jan 2006 18:44:23 -0000 1.29
+++ compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 24 Jan 2006 09:56:39 -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;
@@ -80,9 +81,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);
@@ -90,25 +91,32 @@
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
- if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
+ exitBranch = flowInfo.unconditionalCopy().
+ addInitializationsFrom(condInfo.initsWhenFalse());
+ // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above)
+ if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits &
+ FlowInfo.UNREACHABLE) != 0) {
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();
@@ -132,6 +140,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.52
diff -u -r1.52 IfStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 13 Jan 2006 16:37:16 -0000 1.52
+++ compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java 24 Jan 2006 09:56:39 -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 =
@@ -86,7 +86,7 @@
}
}
// code gen: optimizing the jump around the ELSE part
- this.thenExit = !thenFlowInfo.isReachable();
+ this.thenExit = (thenFlowInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
// process the ELSE part
if (this.elseStatement != null) {
@@ -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.45
diff -u -r1.45 InstanceOfExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 10 Jan 2006 14:37:27 -0000 1.45
+++ compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java 24 Jan 2006 09:56:39 -0000
@@ -31,17 +31,23 @@
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);
+ flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo).
+ unconditionalInits();
+ FlowInfo initsWhenFalse = flowInfo.copy();
+ flowInfo.markAsComparedEqualToNonNull(local);
+ return FlowInfo.conditional(flowInfo, initsWhenFalse);
}
+ 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.53
diff -u -r1.53 LocalDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 10 Jan 2006 14:37:27 -0000 1.53
+++ compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java 24 Jan 2006 09:56:39 -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.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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.107
diff -u -r1.107 MessageSend.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 10 Jan 2006 14:37:27 -0000 1.107
+++ compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java 24 Jan 2006 09:56:39 -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) != Binding.NO_EXCEPTIONS) {
// 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;
@@ -169,7 +174,7 @@
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo){
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return;
// if method from parameterized type got found, use the original method at codegen time
this.codegenBinding = this.binding.original();
Index: compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java,v
retrieving revision 1.52
diff -u -r1.52 MethodDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 10 Jan 2006 14:37:27 -0000 1.52
+++ compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java 24 Jan 2006 09:56:39 -0000
@@ -90,7 +90,8 @@
// check for missing returning path
TypeBinding returnTypeBinding = binding.returnType;
if ((returnTypeBinding == TypeBinding.VOID) || isAbstract()) {
- this.needFreeReturn = flowInfo.isReachable();
+ this.needFreeReturn =
+ (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;
} else {
if (flowInfo != FlowInfo.DEAD_END) {
scope.problemReporter().shouldReturn(returnTypeBinding, this);
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 24 Jan 2006 09:56:39 -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/QualifiedAllocationExpression.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java,v
retrieving revision 1.73
diff -u -r1.73 QualifiedAllocationExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 10 Jan 2006 14:37:27 -0000 1.73
+++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java 24 Jan 2006 09:56:39 -0000
@@ -161,7 +161,7 @@
*/
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
// perform some extra emulation work in case there is some and we are inside a local type only
@@ -175,6 +175,7 @@
currentScope.propagateInnerEmulation(allocatedTypeErasure, enclosingInstance != null);
}
}
+ }
}
public StringBuffer printExpression(int indent, StringBuffer output) {
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.102
diff -u -r1.102 QualifiedNameReference.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 10 Jan 2006 14:37:27 -0000 1.102
+++ compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java 24 Jan 2006 09:56:39 -0000
@@ -93,12 +93,12 @@
.isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) {
currentScope.problemReporter().uninitializedLocalVariable(localBinding, this);
}
- if (flowInfo.isReachable()) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
localBinding.useFlag = LocalVariableBinding.USED;
} 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) {
@@ -252,12 +252,12 @@
.isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) {
currentScope.problemReporter().uninitializedLocalVariable(localBinding, this);
}
- if (flowInfo.isReachable()) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
localBinding.useFlag = LocalVariableBinding.USED;
} 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,25 @@
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)) {
+ if ((this.bits & IsNonNull) == 0) {
+ 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)
*/
@@ -779,9 +797,9 @@
? type.capture(scope, this.sourceEnd)
: type;
}
-
+
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
//If inlinable field, forget the access emulation, the code gen will directly target it
if (((bits & DepthMASK) == 0) || (constant != Constant.NotAConstant)) {
return;
@@ -789,6 +807,7 @@
if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) {
currentScope.emulateOuterAccess((LocalVariableBinding) binding);
}
+ }
}
/**
* index is <0 to denote write access emulation
@@ -799,8 +818,8 @@
TypeBinding lastReceiverType,
int index,
FlowInfo flowInfo) {
-
- if (!flowInfo.isReachable()) return;
+
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return;
// index == 0 denotes the first fieldBinding, index > 0 denotes one of the 'otherBindings', index < 0 denotes a write access (to last binding)
if (fieldBinding.constant() != Constant.NotAConstant)
return;
@@ -855,6 +874,11 @@
}
}
}
+
+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.26
diff -u -r1.26 Reference.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 10 Jan 2006 14:37:27 -0000 1.26
+++ compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java 24 Jan 2006 09:56:39 -0000
@@ -12,7 +12,6 @@
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
-import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
public abstract class Reference extends Expression {
@@ -68,20 +67,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.85
diff -u -r1.85 SingleNameReference.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 10 Jan 2006 14:37:27 -0000 1.85
+++ compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java 24 Jan 2006 09:56:40 -0000
@@ -34,7 +34,7 @@
}
public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) {
- boolean isReachable = flowInfo.isReachable();
+ boolean isReachable = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;
// compound assignment extra work
if (isCompound) { // check the variable part is initialized if blank final
switch (bits & RestrictiveFlagMASK) {
@@ -163,7 +163,7 @@
if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) {
currentScope.problemReporter().uninitializedLocalVariable(localBinding, this);
}
- if (flowInfo.isReachable()) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
localBinding.useFlag = LocalVariableBinding.USED;
} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
localBinding.useFlag = LocalVariableBinding.FAKE_USED;
@@ -641,17 +641,18 @@
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
//If inlinable field, forget the access emulation, the code gen will directly target it
if (((bits & DepthMASK) == 0) || (constant != Constant.NotAConstant)) return;
if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) {
currentScope.emulateOuterAccess((LocalVariableBinding) binding);
}
+ }
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return;
//If inlinable field, forget the access emulation, the code gen will directly target it
if (constant != Constant.NotAConstant)
@@ -695,6 +696,28 @@
}
}
}
+
+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;
+// REVIEW should never get here?
+}
+
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope)
*/
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.62
diff -u -r1.62 SwitchStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 13 Jan 2006 16:37:16 -0000 1.62
+++ compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java 24 Jan 2006 09:56:40 -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/SynchronizedStatement.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java,v
retrieving revision 1.39
diff -u -r1.39 SynchronizedStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java 13 Jan 2006 16:37:16 -0000 1.39
+++ compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java 24 Jan 2006 09:56:40 -0000
@@ -56,7 +56,7 @@
expression.analyseCode(scope, flowContext, flowInfo));
// optimizing code gen
- this.blockExit = !flowInfo.isReachable();
+ this.blockExit = (flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
return flowInfo;
}
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.86
diff -u -r1.86 TryStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 13 Jan 2006 16:37:16 -0000 1.86
+++ compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java 24 Jan 2006 09:56:40 -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;
@@ -108,7 +108,7 @@
tryBlockExit = false;
} else {
tryInfo = tryBlock.analyseCode(currentScope, handlingContext, flowInfo.copy());
- tryBlockExit = !tryInfo.isReachable();
+ tryBlockExit = (tryInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
}
// check unreachable catch blocks
@@ -121,13 +121,17 @@
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.nullInfoLessUnconditionalCopy())
+ // remove null info to protect point of
+ // exception null info
+ .addPotentialInitializationsFrom(
+ handlingContext.initsOnReturn.
+ nullInfoLessUnconditionalCopy());
// catch var is always set
LocalVariableBinding catchArg = catchArguments[i].binding;
@@ -149,7 +153,8 @@
currentScope,
catchContext,
catchInfo);
- catchExits[i] = !catchInfo.isReachable();
+ catchExits[i] =
+ (catchInfo.tagBits & FlowInfo.UNREACHABLE) != 0;
tryInfo = tryInfo.mergedWith(catchInfo.unconditionalInits());
}
}
@@ -162,10 +167,16 @@
// 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(
- tryInfo.isReachable()
- ? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
- : insideSubContext.initsOnReturn,
+ finallyContext/* NN null with subRoutineStartLabel, which returns */.complainOnDeferredChecks(
+ (tryInfo.tagBits & FlowInfo.UNREACHABLE) == 0
+ ? flowInfo.unconditionalCopy().
+ addPotentialInitializationsFrom(tryInfo).
+ // lighten the influence of the try block, which may have
+ // exited at any point
+ addPotentialInitializationsFrom(
+ insideSubContext/* NN null with subRoutineStartLabel, which returns */.
+ initsOnReturn)
+ : insideSubContext.initsOnReturn,
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.112
diff -u -r1.112 TypeDeclaration.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 10 Jan 2006 14:37:27 -0000 1.112
+++ compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java 24 Jan 2006 09:56:40 -0000
@@ -201,7 +201,7 @@
if (ignoreFurtherInvestigation)
return flowInfo;
try {
- if (flowInfo.isReachable()) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
bits |= IsReachable;
LocalTypeBinding localType = (LocalTypeBinding) binding;
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
@@ -244,7 +244,7 @@
if (ignoreFurtherInvestigation)
return;
try {
- if (flowInfo.isReachable()) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
bits |= IsReachable;
LocalTypeBinding localType = (LocalTypeBinding) binding;
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
@@ -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,13 +644,13 @@
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];
if (field.isStatic()) {
- if (!staticFieldInfo.isReachable())
+ if ((staticFieldInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
field.bits &= ~ASTNode.IsReachable;
/*if (field.isField()){
@@ -670,7 +670,7 @@
staticFieldInfo = FlowInfo.initial(maxFieldCount).setReachMode(FlowInfo.UNREACHABLE);
}
} else {
- if (!nonStaticFieldInfo.isReachable())
+ if ((nonStaticFieldInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
field.bits &= ~ASTNode.IsReachable;
/*if (field.isField()){
@@ -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];
@@ -748,7 +748,7 @@
*/
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return;
NestedTypeBinding nestedType = (NestedTypeBinding) binding;
MethodScope methodScope = currentScope.methodScope();
@@ -800,9 +800,10 @@
*/
public void manageEnclosingInstanceAccessIfNecessary(ClassScope currentScope, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
NestedTypeBinding nestedType = (NestedTypeBinding) binding;
nestedType.addSyntheticArgumentAndField(binding.enclosingType());
+ }
}
/**
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.39
diff -u -r1.39 UnaryExpression.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 10 Jan 2006 14:37:27 -0000 1.39
+++ compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java 24 Jan 2006 09:56:40 -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.54
diff -u -r1.54 WhileStatement.java
--- compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 13 Jan 2006 16:37:16 -0000 1.54
+++ compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java 24 Jan 2006 09:56:40 -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,28 @@
}
// 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
- if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
+ exitBranch = flowInfo.copy();
+ // need to start over from flowInfo so as to get null inits
+
+ if ((actionInfo.tagBits &
+ loopingContext.initsOnContinue.tagBits &
+ FlowInfo.UNREACHABLE) != 0) {
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/codegen/CodeStream.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java,v
retrieving revision 1.121
diff -u -r1.121 CodeStream.java
--- compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 20 Jan 2006 03:44:05 -0000 1.121
+++ compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java 24 Jan 2006 09:56:41 -0000
@@ -4549,7 +4549,7 @@
bCodeStream[classFileOffset++] = Opcodes.OPC_ireturn;
}
public boolean isDefinitelyAssigned(Scope scope, int initStateIndex, LocalVariableBinding local) {
- // Dependant of UnconditionalFlowInfo.isDefinitelyAssigned(..)
+ // Mirror of UnconditionalFlowInfo.isDefinitelyAssigned(..)
if (initStateIndex == -1)
return false;
if (local.isArgument) {
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 24 Jan 2006 09:56:41 -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:
- *
intersection of definitely assigned variables,
- *
union of potentially assigned variables.
- *
- */
- 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"; //$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.34
diff -u -r1.34 ExceptionHandlingFlowContext.java
--- compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 10 Jan 2006 14:37:28 -0000 1.34
+++ compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java 24 Jan 2006 09:56:41 -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,21 @@
this.isReached[cacheIndex] |= bitMask;
initsOnExceptions[index] =
- initsOnExceptions[index] == FlowInfo.DEAD_END
- ? flowInfo.copy().unconditionalInits()
- : initsOnExceptions[index].mergedWith(flowInfo.copy().unconditionalInits());
+ (initsOnExceptions[index].tagBits & FlowInfo.UNREACHABLE) == 0 ?
+ 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.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ if ((initsOnReturn.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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 24 Jan 2006 09:56:41 -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,72 @@
return true;
}
+ public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
+ Expression reference, int checkType, FlowInfo flowInfo) {
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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 +262,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.46
diff -u -r1.46 FlowContext.java
--- compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 10 Jan 2006 14:37:28 -0000 1.46
+++ compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java 24 Jan 2006 09:56:41 -0000
@@ -36,12 +36,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() {
@@ -164,7 +167,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;
}
@@ -257,7 +260,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;
}
@@ -404,7 +407,7 @@
// default implementation: do nothing
}
-public void recordContinueFrom(FlowInfo flowInfo) {
+public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) {
// default implementation: do nothing
}
@@ -412,17 +415,26 @@
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
}
public void recordSettingFinal(VariableBinding variable, Reference finalReference, FlowInfo flowInfo) {
- if (!flowInfo.isReachable()) return;
-
+ if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// for initialization inside looping statement that effectively loops
FlowContext context = this;
while (context != null) {
@@ -431,36 +443,76 @@
}
context = context.parent;
}
+ }
}
-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.tagBits & FlowInfo.UNREACHABLE) != 0 ||
+ 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 24 Jan 2006 09:56:41 -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,44 @@
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);
+
+/**
+ * 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 +199,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 +208,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();
}
- return mergedInfo;
+ }
+ else if (isOptimizedFalse) {
+ if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
+ mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE).
+ unconditionalInits();
+ }
+ else {
+ mergedInfo =
+ initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue.
+ nullInfoLessUnconditionalCopy()).
+ unconditionalInits();
+ }
+ }
+ 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:
- *
intersection of definitely assigned variables,
- *
union of potentially assigned variables.
- *
- */
- 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.
+ * 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:
+ *
the receiver updated in the following way:
+ *
intersection of definitely assigned variables,
+ *
union of potentially assigned variables,
+ *
similar operations for null,
+ *
or the receiver or otherInits if the other one is non
+ * reachable.
+ * 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 +310,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 24 Jan 2006 09:56:41 -0000
@@ -47,13 +47,13 @@
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.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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 24 Jan 2006 09:56:42 -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,45 @@
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.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ if ((initsOnContinue.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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 +283,68 @@
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.tagBits & FlowInfo.UNREACHABLE) != 0 ||
+ 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 24 Jan 2006 09:56:42 -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.tagBits & FlowInfo.UNREACHABLE) == 0) {
+ 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 24 Jan 2006 09:56:42 -0000
@@ -10,10 +10,12 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
+import org.eclipse.jdt.internal.core.Assert.AssertionFailedException; // for coverage tests
import org.eclipse.jdt.internal.compiler.impl.Constant;
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
@@ -21,808 +23,1700 @@
* No caching of pre-allocated instances.
*/
public class UnconditionalFlowInfo extends FlowInfo {
+ // Coverage tests
+ // Coverage tests need that the code be instrumented. The following flag
+ // controls whether the instrumented code is compiled in or not, and whether
+ // the coverage tests methods run or not.
+ public final static boolean coverageTestFlag = false;
+ // never release with the coverageTestFlag set to true
+ public static int coverageTestId;
-
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
+ // REVIEW consider reintroducing the difference between potential non null and potential
+ // REVIEW unknown; if this is done, rename to nullAssignmentBit[1-4] since the semantics
+ // REVIEW would be ever less clear
+ // REVIEW went public in order to grant access to tests; do not like it...
+
+ 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: + -->
-
- // 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;
+ // REVIEW does an inner declaration save stack space? does duplicate declaration waste time?
+ 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;
+ if (coverageTestFlag && coverageTestId == 1) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ else {
+ // TODO (maxime) indent as follows:
+ /*
+ * a
+ * | (b
+ * & c)
+ *
+ */
+ // REVIEW indentation example
+ 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));
+ if (coverageTestFlag && coverageTestId == 2) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ 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)) {
- // 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];
+ int length, otherLength;
+ if ((length = this.extra[0].length) <
+ (otherLength = otherInits.extra[0].length)) {
+ if (coverageTestFlag && coverageTestId == 3) {
+ throw new AssertionFailedException("COVERAGE 3"); //$NON-NLS-1$
}
- for (; i < otherLength; i++) {
- extraPotentialInits[i] = otherInits.extraPotentialInits[i];
+ // 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);
}
+ mergeLimit = length;
+ copyLimit = otherLength;
} else {
+ if (coverageTestFlag && coverageTestId == 4) {
+ throw new AssertionFailedException("COVERAGE 4"); //$NON-NLS-1$
+ }
// 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;
- }
- }
- } else {
- // no extra storage on otherInits
- }
- } 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);
+ 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);
+ }
+ if (coverageTestFlag && coverageTestId == 5) {
+ this.extra[5][otherLength - 1] = ~0;
+ }
+ }
+ else {
+ for (int j = 2; j < extraLength; j++) {
+ this.extra[j] = new long[otherLength];
+ }
+ if (coverageTestFlag && coverageTestId == 6) {
+ this.extra[5][otherLength - 1] = ~0;
+ }
}
- return this;
- }
-
- // unions of both sets of initialization - used for try/finally
- public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
-
- if (this == DEAD_END){
- 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++];
- }
- while (i < otherLength) {
- this.extraPotentialInits[i] = otherInits.extraPotentialInits[i];
- this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
- this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
+ 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 {
- // 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++];
+ if (coverageTestFlag && coverageTestId == 7) {
+ this.extra[5][i] = ~0;
}
}
+ 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));
+ if (coverageTestFlag && coverageTestId == 8) {
+ this.extra[5][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];
+ }
+ 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];
+ }
+ if (coverageTestFlag && coverageTestId == 9) {
+ 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;
- }
+ 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;
+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);
+ // REVIEW inline?
+ 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
+/**
+ * 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);
+ if (coverageTestFlag && coverageTestId == 15) {
+ 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) {
+ this.extra = new long[extraLength][];
+ for (int j = 0; j < extraLength; j++) {
+ this.extra[j] = new long[otherLength];
+ }
+ copyLimit = otherLength;
+ if (coverageTestFlag && coverageTestId == 16) {
+ this.extra[2][0] = ~0; thisHasNulls = true;
+ }
+ }
+ 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);
+ }
+ }
+ 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);
+ if (coverageTestFlag && coverageTestId == 17) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ 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];
+ if (coverageTestFlag && coverageTestId == 18) {
+ this.extra[5][i] = ~0;
+ }
+ }
+ }
+ }
}
- int vectorIndex, length = this.extraDefiniteInits.length;
- if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
- return this; // not enough room yet
+ }
+ 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;
+ if (coverageTestFlag && coverageTestId == 10) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
}
- 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;
+ // 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];
+ }
+ if (coverageTestFlag && coverageTestId == 11) {
+ this.extra[5][0] = ~0; this.tagBits |= NULL_FLAG_MASK;
+ }
+ }
+ 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];
+ }
+ if (coverageTestFlag && coverageTestId == 12) {
+ throw new AssertionFailedException("COVERAGE 12"); //$NON-NLS-1$
+ }
+ }
+ }
+ 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;
+ if (coverageTestFlag && coverageTestId == 13) {
+ 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;
+ if (coverageTestFlag && coverageTestId == 14) {
+ this.extra[5][i] = ~0;
+ }
+ }
+ }
}
- 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;
- }
- return this;
+ if (thisHasNulls) {
+ this.tagBits |= NULL_FLAG_MASK;
}
-
- public FlowInfo initsWhenFalse() {
-
- return this;
+ else {
+ this.tagBits &= NULL_FLAG_MASK;
}
-
- public FlowInfo initsWhenTrue() {
-
+ return this;
+}
+
+public FlowInfo copy() {
+ // do not clone the DeadEnd
+ if (this == DEAD_END) {
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) {
-
- // Dependant of CodeStream.isDefinitelyAssigned(..)
- // id is zero-based
- if (position < BitCacheSize) {
- return (definiteInits & (1L << position)) != 0; // use bits
+ 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);
+ }
}
- // 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
+ else {
+ for (int j = 2; j < extraLength; j++) {
+ copy.extra[j] = new long[length];
+ }
}
- // 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
+ }
+ return copy;
+}
+
+/**
+ * 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
+ }
+ 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 (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;
+ for (int i = vectorIndex + 1; i < length; i++) {
+ for (int j = 0; j < extraLength; j++) {
+ this.extra[j][i] = 0;
}
- return isDefinitelyAssigned(local.id + maxFieldCount);
}
-
- /**
- * 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;
+}
+
+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 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 ((this.extra[0][vectorIndex]) &
+ (1L << (position % BitCacheSize))) != 0;
+}
+
+final public boolean isDefinitelyAssigned(FieldBinding field) {
+ // Mirrored in CodeStream.isDefinitelyAssigned(..)
+ // do not want to complain in unreachable code
+ if ((this.tagBits & UNREACHABLE) != 0) {
+ 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(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;
}
+ 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) {
+ // REVIEW only true if local is of a non object type, hence
+ // REVIEW second test is useless?
+ 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;
+}
- public boolean isReachable() {
-
- return this.reachMode == REACHABLE;
+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.
+ */
+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
}
-
- /**
- * 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 ((this.extra[1][vectorIndex]) &
+ (1L << (position % BitCacheSize))) != 0;
+}
+
+/**
+ * REVIEW wrong comment?
+ * 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;
+ }
+ return isPotentiallyAssigned(local.id + this.maxFieldCount);
+}
+
+// REVIEW should rename this -- what we do is that we ask if there is a reasonable
+// REVIEW expectation that the variable be null at this point; which means that
+// REVIEW we add the protected null case, to augment diagnostics, but we do not
+// REVIEW really check that someone deliberately has assigned to null on a given
+// REVIEW path
+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;
+ if (coverageTestFlag && coverageTestId == 19) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ if (coverageTestFlag && coverageTestId == 20) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ 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];
+ }
+ if (coverageTestFlag && coverageTestId == 21) {
+ throw new AssertionFailedException("COVERAGE 21"); //$NON-NLS-1$
+ }
+ }
+ 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 (coverageTestFlag && coverageTestId == 22) {
+ throw new AssertionFailedException("COVERAGE 22"); //$NON-NLS-1$
+ }
+ }
+ }
+ 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;
+ if (coverageTestFlag && coverageTestId == 23) {
+ this.extra[5][vectorIndex] = ~0;
+ }
+ }
}
- // 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;
+}
+
+// REVIEW javadoc policy?
+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
+ // REVIEW coûts relatifs d'un assignment et d'une négation?
+ if (coverageTestFlag && coverageTestId == 24) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ if (coverageTestFlag && coverageTestId == 25) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ 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 ];
+ }
+ if (coverageTestFlag && coverageTestId == 26) {
+ throw new AssertionFailedException("COVERAGE 26"); //$NON-NLS-1$
+ }
+ }
+ 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 (coverageTestFlag && coverageTestId == 27) {
+ throw new AssertionFailedException("COVERAGE 27"); //$NON-NLS-1$
+ }
+ }
+ }
+ 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;
+ if (coverageTestFlag && coverageTestId == 28) {
+ this.extra[5][vectorIndex] = ~0;
+ }
+ }
}
- return isPotentiallyAssigned(local.id + maxFieldCount);
}
+}
+
+/**
+ * Record a definite assignment at a given position.
+ * REVIEW wrong comment?
+ * 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);
- }
-
- /**
- * 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 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
+ if (coverageTestFlag && coverageTestId == 29) {
+ this.nullAssignmentStatusBit1 = 0;
+ }
+ }
+ else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ // REVIEW seems to be guarded
+ this.extra[2][vectorIndex] |=
+ (mask = 1L << (position % BitCacheSize));
+ this.extra[5][vectorIndex] |= mask;
+ this.extra[3][vectorIndex] &= ~mask;
+ this.extra[4][vectorIndex] &= ~mask;
+ if (coverageTestFlag && coverageTestId == 30) {
+ this.extra[5][vectorIndex] = ~0;
}
}
+}
- /**
- * 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(FieldBinding field) {
+ if (this != DEAD_END) {
+ markAsDefinitelyNonNull(field.id);
}
-
- /**
- * 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;
- }
+}
+
+public void markAsDefinitelyNonNull(LocalVariableBinding local) {
+ // protected from non-object locals in calling methods
+ if (this != DEAD_END) {
+ markAsDefinitelyNonNull(local.id + this.maxFieldCount);
+ }
+}
+
+/**
+ * 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
+ if (coverageTestFlag && coverageTestId == 31) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ // REVIEW seems to be guarded
+ this.extra[2][vectorIndex] |=
+ (mask = 1L << (position % BitCacheSize));
+ this.extra[3][vectorIndex] &= ~mask;
+ this.extra[4][vectorIndex] |= mask;
+ this.extra[5][vectorIndex] &= ~mask;
+ if (coverageTestFlag && coverageTestId == 32) {
+ this.extra[5][vectorIndex] = ~0;
}
}
+}
- /**
- * 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
+ if (coverageTestFlag && coverageTestId == 33) {
+ this.nullAssignmentValueBit2 = ~0;
+ }
+ }
+ else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ // REVIEW seems to be guarded
+ this.extra[4][vectorIndex] |=
+ (mask = 1L << (position % BitCacheSize));
+ this.extra[5][vectorIndex] |= mask;
+ this.extra[2][vectorIndex] |= mask;
+ this.extra[3][vectorIndex] &= ~mask;
+ if (coverageTestFlag && coverageTestId == 34) {
+ this.extra[5][vectorIndex] = ~0;
}
}
}
-
- /**
- * Clear the initialization info for a field
- */
- public void markAsDefinitelyNotAssigned(FieldBinding field) {
-
- if (this != DEAD_END)
- markAsDefinitelyNotAssigned(field.id);
- }
-
- /**
- * 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:
- *
intersection of definitely assigned variables,
- *