View | Details | Raw Unified | Return to bug 110030 | Differences between
and this patch

Collapse All | Expand All

(-)compiler/org/eclipse/jdt/core/compiler/IProblem.java (+2 lines)
Lines 684-689 Link Here
684
	int LocalVariableCannotBeNull = MethodRelated + 397;
684
	int LocalVariableCannotBeNull = MethodRelated + 397;
685
	/** @since 3.1 */
685
	/** @since 3.1 */
686
	int LocalVariableCanOnlyBeNull = MethodRelated + 398;
686
	int LocalVariableCanOnlyBeNull = MethodRelated + 398;
687
	/** @since 3.2 */
688
	int LocalVariableMayBeNull = MethodRelated + 399;
687
689
688
	// method verifier problems
690
	// method verifier problems
689
	int AbstractMethodMustBeImplemented = MethodRelated + 400;
691
	int AbstractMethodMustBeImplemented = MethodRelated + 400;
(-)compiler/org/eclipse/jdt/internal/compiler/ast/AND_AND_Expression.java (-6 / +5 lines)
Lines 49-55 Link Here
49
		// need to be careful of scenario:
49
		// need to be careful of scenario:
50
		//  (x && y) && !z, if passing the left info to the right, it would be
50
		//  (x && y) && !z, if passing the left info to the right, it would be
51
		// swapped by the !
51
		// swapped by the !
52
		FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalInits().copy();
52
		FlowInfo rightInfo = leftInfo.initsWhenTrue().unconditionalCopy();
53
		rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo);
53
		rightInitStateIndex = currentScope.methodScope().recordInitializationStates(rightInfo);
54
54
55
		int previousMode = rightInfo.reachMode();
55
		int previousMode = rightInfo.reachMode();
Lines 57-68 Link Here
57
			rightInfo.setReachMode(FlowInfo.UNREACHABLE);
57
			rightInfo.setReachMode(FlowInfo.UNREACHABLE);
58
		}
58
		}
59
		rightInfo = right.analyseCode(currentScope, flowContext, rightInfo);
59
		rightInfo = right.analyseCode(currentScope, flowContext, rightInfo);
60
		FlowInfo trueMergedInfo = rightInfo.initsWhenTrue().copy();
61
		rightInfo.setReachMode(previousMode); // reset after trueMergedInfo got extracted
62
		FlowInfo mergedInfo = FlowInfo.conditional(
60
		FlowInfo mergedInfo = FlowInfo.conditional(
63
				trueMergedInfo, 
61
				rightInfo.safeInitsWhenTrue(), 
64
				leftInfo.initsWhenFalse().copy().unconditionalInits().mergedWith(
62
				leftInfo.initsWhenFalse().unconditionalInits().mergedWith(
65
						rightInfo.initsWhenFalse().copy().unconditionalInits()));
63
						rightInfo.initsWhenFalse().setReachMode(previousMode).unconditionalInits()));
64
		// reset after trueMergedInfo got extracted
66
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
65
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
67
		return mergedInfo;
66
		return mergedInfo;
68
	}
67
	}
(-)compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java (-19 / +21 lines)
Lines 27-60 Link Here
27
		sourceStart = rec.sourceStart;
27
		sourceStart = rec.sourceStart;
28
	}
28
	}
29
29
30
	public FlowInfo analyseAssignment(
30
public FlowInfo analyseAssignment(
31
		BlockScope currentScope,
31
		BlockScope currentScope,
32
		FlowContext flowContext,
32
		FlowContext flowContext,
33
		FlowInfo flowInfo,
33
		FlowInfo flowInfo,
34
		Assignment assignment,
34
		Assignment assignment,
35
		boolean compoundAssignment) {
35
		boolean compoundAssignment) {
36
	// unconditionalInits is applied to all existing calls -> we remove extraneous ones here
37
	if (assignment.expression == null) {
38
		return analyseCode(currentScope, flowContext, flowInfo);
39
	}
40
	return assignment
41
		.expression
42
		.analyseCode(
43
			currentScope,
44
			flowContext,
45
			analyseCode(currentScope, flowContext, flowInfo).unconditionalInits());
46
}
36
47
37
		if (assignment.expression == null) {
48
public FlowInfo analyseCode(
38
			return analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
39
		}
40
		return assignment
41
			.expression
42
			.analyseCode(
43
				currentScope,
44
				flowContext,
45
				analyseCode(currentScope, flowContext, flowInfo).unconditionalInits())
46
			.unconditionalInits();
47
	}
48
49
	public FlowInfo analyseCode(
50
		BlockScope currentScope,
49
		BlockScope currentScope,
51
		FlowContext flowContext,
50
		FlowContext flowContext,
52
		FlowInfo flowInfo) {
51
		FlowInfo flowInfo) {
53
52
	receiver.checkNPE(currentScope, flowContext, flowInfo, true);
54
		flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo);
53
	flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo);
55
		receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
54
	return position.analyseCode(currentScope, flowContext, flowInfo);
56
		return position.analyseCode(currentScope, flowContext, flowInfo);
55
}
57
	}
58
56
59
	public void generateAssignment(
57
	public void generateAssignment(
60
		BlockScope currentScope,
58
		BlockScope currentScope,
Lines 179-184 Link Here
179
		codeStream.arrayAtPut(this.resolvedType.id, false);
177
		codeStream.arrayAtPut(this.resolvedType.id, false);
180
	}
178
	}
181
179
180
public int nullStatus(FlowInfo flowInfo) {
181
	return FlowInfo.UNKNOWN;
182
}
183
182
	public StringBuffer printExpression(int indent, StringBuffer output) {
184
	public StringBuffer printExpression(int indent, StringBuffer output) {
183
185
184
		receiver.printExpression(0, output).append('[');
186
		receiver.printExpression(0, output).append('[');
(-)compiler/org/eclipse/jdt/internal/compiler/ast/AssertStatement.java (-3 / +4 lines)
Lines 54-64 Link Here
54
		boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
54
		boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true;
55
		boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;
55
		boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false;
56
56
57
		FlowInfo assertInfo = flowInfo.copy();
57
		UnconditionalFlowInfo assertInfo = assertExpression.
58
			analyseCode(currentScope, flowContext, flowInfo.copy()).
59
			unconditionalInits();
58
		if (isOptimizedTrueAssertion) {
60
		if (isOptimizedTrueAssertion) {
59
			assertInfo.setReachMode(FlowInfo.UNREACHABLE);
61
			assertInfo.setReachMode(FlowInfo.UNREACHABLE);
60
		}
62
		}
61
		assertInfo = assertExpression.analyseCode(currentScope, flowContext, assertInfo).unconditionalInits();
62
		
63
		
63
		if (exceptionArgument != null) {
64
		if (exceptionArgument != null) {
64
			// only gets evaluated when escaping - results are not taken into account
65
			// only gets evaluated when escaping - results are not taken into account
Lines 80-86 Link Here
80
		if (isOptimizedFalseAssertion) {
81
		if (isOptimizedFalseAssertion) {
81
			return flowInfo; // if assertions are enabled, the following code will be unreachable
82
			return flowInfo; // if assertions are enabled, the following code will be unreachable
82
		} else {
83
		} else {
83
			return flowInfo.mergedWith(assertInfo.unconditionalInits()); 
84
			return flowInfo.mergedWith(assertInfo); 
84
		}
85
		}
85
	}
86
	}
86
87
(-)compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java (-28 / +36 lines)
Lines 36-69 Link Here
36
		this.sourceEnd = sourceEnd;
36
		this.sourceEnd = sourceEnd;
37
	}
37
	}
38
38
39
	public FlowInfo analyseCode(
39
public FlowInfo analyseCode(
40
		BlockScope currentScope,
40
	BlockScope currentScope,
41
		FlowContext flowContext,
41
	FlowContext flowContext,
42
		FlowInfo flowInfo) {
42
	FlowInfo flowInfo) {
43
		// record setting a variable: various scenarii are possible, setting an array reference, 
43
	// record setting a variable: various scenarii are possible, setting an array reference, 
44
		// a field reference, a blank final field reference, a field of an enclosing instance or 
44
	// a field reference, a blank final field reference, a field of an enclosing instance or 
45
		// just a local variable.
45
	// just a local variable.
46
46
	LocalVariableBinding local = this.lhs.localVariableBinding();
47
		LocalVariableBinding local = this.lhs.localVariableBinding();
47
	int nullStatus = this.expression.nullStatus(flowInfo);
48
		int nullStatus = this.expression.nullStatus(flowInfo);
48
	if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
49
		if (local != null && nullStatus == FlowInfo.NULL) {
49
		if (nullStatus == FlowInfo.NULL) {
50
				flowContext.recordUsingNullReference(currentScope, local, this.lhs, FlowInfo.NON_NULL, flowInfo);
50
			flowContext.recordUsingNullReference(currentScope, local, this.lhs, 
51
		}
51
				FlowContext.CAN_ONLY_NULL, flowInfo);
52
		flowInfo = ((Reference) lhs)
52
		}
53
			.analyseAssignment(currentScope, flowContext, flowInfo, this, false)
53
	}
54
			.unconditionalInits();
54
	flowInfo = ((Reference) lhs)
55
		if (local != null) {
55
		.analyseAssignment(currentScope, flowContext, flowInfo, this, false)
56
			switch(nullStatus) {
56
		.unconditionalInits();
57
				case FlowInfo.NULL :
57
	if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
58
					flowInfo.markAsDefinitelyNull(local);
58
		switch(nullStatus) {
59
					break;
59
			case FlowInfo.NULL :
60
				case FlowInfo.NON_NULL :
60
				flowInfo.markAsDefinitelyNull(local);
61
					flowInfo.markAsDefinitelyNonNull(local);
61
				break;
62
					break;
62
			case FlowInfo.NON_NULL :
63
			}
63
				flowInfo.markAsDefinitelyNonNull(local);
64
		}		
64
				break;
65
		return flowInfo;
65
			default:
66
	}
66
				flowInfo.markAsDefinitelyUnknown(local);
67
		}
68
	}		
69
	return flowInfo;
70
}
67
71
68
	void checkAssignmentEffect(BlockScope scope) {
72
	void checkAssignmentEffect(BlockScope scope) {
69
		
73
		
Lines 250-253 Link Here
250
		}
254
		}
251
		visitor.endVisit(this, scope);
255
		visitor.endVisit(this, scope);
252
	}
256
	}
257
258
public LocalVariableBinding localVariableBinding() {
259
	return lhs.localVariableBinding();
260
}
253
}
261
}
(-)compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java (-9 / +8 lines)
Lines 31-48 Link Here
31
		this.sourceEnd = right.sourceEnd;
31
		this.sourceEnd = right.sourceEnd;
32
	}
32
	}
33
33
34
	public FlowInfo analyseCode(
34
public FlowInfo analyseCode(
35
		BlockScope currentScope,
35
		BlockScope currentScope,
36
		FlowContext flowContext,
36
		FlowContext flowContext,
37
		FlowInfo flowInfo) {
37
		FlowInfo flowInfo) {
38
38
	left.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */);
39
		return right
39
	flowInfo = left.analyseCode(currentScope, flowContext, flowInfo).
40
			.analyseCode(
40
		unconditionalInits();
41
				currentScope,
41
	right.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */);
42
				flowContext,
42
	return right.analyseCode(currentScope, flowContext, flowInfo).
43
				left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits())
43
		unconditionalInits();
44
			.unconditionalInits();
44
}
45
	}
46
45
47
	public void computeConstant(BlockScope scope, int leftId, int rightId) {
46
	public void computeConstant(BlockScope scope, int leftId, int rightId) {
48
47
(-)compiler/org/eclipse/jdt/internal/compiler/ast/CompoundAssignment.java (-10 / +12 lines)
Lines 34-46 Link Here
34
		this.operator = operator ;
34
		this.operator = operator ;
35
	}
35
	}
36
	
36
	
37
	public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
37
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, 
38
		// record setting a variable: various scenarii are possible, setting an array reference, 
38
		FlowInfo flowInfo) {
39
		// a field reference, a blank final field reference, a field of an enclosing instance or 
39
	// record setting a variable: various scenarii are possible, setting an array reference, 
40
		// just a local variable.
40
	// a field reference, a blank final field reference, a field of an enclosing instance or 
41
	
41
	// just a local variable.
42
		return  ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits();
42
	lhs.checkNPE(currentScope, flowContext, flowInfo, false /* skip String */);
43
	}
43
	return  ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits();
44
}
44
	
45
	
45
	public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
46
	public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
46
	
47
	
Lines 56-64 Link Here
56
		codeStream.recordPositionsFrom(pc, this.sourceStart);
57
		codeStream.recordPositionsFrom(pc, this.sourceStart);
57
	}
58
	}
58
	
59
	
59
	public int nullStatus(FlowInfo flowInfo) {
60
public int nullStatus(FlowInfo flowInfo) {
60
		return FlowInfo.NON_NULL;
61
	return FlowInfo.NON_NULL;
61
	}
62
	// we may have complained on checkNPE, but we avoid duplicate error 
63
}
62
	
64
	
63
	public String operatorToString() {
65
	public String operatorToString() {
64
		switch (operator) {
66
		switch (operator) {
(-)compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java (-11 / +22 lines)
Lines 40-50 Link Here
40
		sourceEnd = valueIfFalse.sourceEnd;
40
		sourceEnd = valueIfFalse.sourceEnd;
41
	}
41
	}
42
42
43
	public FlowInfo analyseCode(
43
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
44
		BlockScope currentScope,
45
		FlowContext flowContext,
46
		FlowInfo flowInfo) {
44
		FlowInfo flowInfo) {
47
48
		Constant cst = this.condition.optimizedBooleanConstant();
45
		Constant cst = this.condition.optimizedBooleanConstant();
49
		boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
46
		boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
50
		boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
47
		boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
Lines 84-99 Link Here
84
			boolean isValueIfFalseOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true;
81
			boolean isValueIfFalseOptimizedTrue = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == true;
85
			boolean isValueIfFalseOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false;
82
			boolean isValueIfFalseOptimizedFalse = cst != null && cst != Constant.NotAConstant && cst.booleanValue() == false;
86
83
87
			UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().copy().unconditionalInits();
84
			UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo.initsWhenTrue().unconditionalCopy();
85
			UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().unconditionalCopy();
86
			UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().unconditionalInits();
87
			UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().unconditionalInits();
88
			if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
88
			if (isValueIfTrueOptimizedFalse) trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
89
90
			UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo.initsWhenTrue().copy().unconditionalInits();
91
			if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
89
			if (isValueIfFalseOptimizedFalse) falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); 
92
			
93
			UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo.initsWhenFalse().copy().unconditionalInits();
94
			if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
90
			if (isValueIfTrueOptimizedTrue) trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
95
96
			UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo.initsWhenFalse().copy().unconditionalInits();
97
			if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
91
			if (isValueIfFalseOptimizedTrue) falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); 
98
92
99
			mergedInfo =
93
			mergedInfo =
Lines 262-267 Link Here
262
		// no implicit conversion for boolean values
256
		// no implicit conversion for boolean values
263
		codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
257
		codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
264
	}
258
	}
259
	
260
public int nullStatus(FlowInfo flowInfo) {
261
	Constant cst = this.condition.optimizedBooleanConstant();
262
	if (cst != Constant.NotAConstant) {
263
		if (cst.booleanValue()) {
264
			return valueIfTrue.nullStatus(flowInfo);
265
		}
266
		return valueIfFalse.nullStatus(flowInfo);
267
	}
268
	int ifTrueNullStatus = valueIfTrue.nullStatus(flowInfo),
269
	    ifFalseNullStatus = valueIfFalse.nullStatus(flowInfo);
270
	if (ifTrueNullStatus == ifFalseNullStatus) {
271
		return ifTrueNullStatus;
272
	}
273
	return FlowInfo.UNKNOWN;
274
	// cannot decide which branch to take, and they disagree
275
}
265
276
266
	public Constant optimizedBooleanConstant() {
277
	public Constant optimizedBooleanConstant() {
267
278
(-)compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java (-1 / +1 lines)
Lines 71-77 Link Here
71
				flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits			
71
				flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits			
72
			} else if (traversedContext == targetContext) {
72
			} else if (traversedContext == targetContext) {
73
				// only record continue info once accumulated through subroutines, and only against target context
73
				// only record continue info once accumulated through subroutines, and only against target context
74
				targetContext.recordContinueFrom(flowInfo);
74
				targetContext.recordContinueFrom(flowContext, flowInfo);
75
				break;
75
				break;
76
			}
76
			}
77
		} while ((traversedContext = traversedContext.parent) != null);
77
		} while ((traversedContext = traversedContext.parent) != null);
(-)compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java (-8 / +26 lines)
Lines 46-51 Link Here
46
		LoopingFlowContext loopingContext =
46
		LoopingFlowContext loopingContext =
47
			new LoopingFlowContext(
47
			new LoopingFlowContext(
48
				flowContext,
48
				flowContext,
49
				flowInfo,
49
				this,
50
				this,
50
				breakLabel,
51
				breakLabel,
51
				continueLabel,
52
				continueLabel,
Lines 59-67 Link Here
59
60
60
		int previousMode = flowInfo.reachMode();
61
		int previousMode = flowInfo.reachMode();
61
				
62
				
62
		FlowInfo actionInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
63
		UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy();
64
		// we need to collect the contribution to nulls of the coming paths through the
65
		// loop, be they falling through normally or branched to break, continue labels
66
		// or catch blocks
63
		if ((action != null) && !action.isEmptyBlock()) {
67
		if ((action != null) && !action.isEmptyBlock()) {
64
			actionInfo = action.analyseCode(currentScope, loopingContext, actionInfo);
68
			actionInfo = action.
69
				analyseCode(currentScope, loopingContext, actionInfo).
70
				unconditionalInits();
65
71
66
			// code generation can be optimized when no need to continue in the loop
72
			// code generation can be optimized when no need to continue in the loop
67
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
73
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
Lines 75-96 Link Here
75
		 */
81
		 */
76
		actionInfo.setReachMode(previousMode);
82
		actionInfo.setReachMode(previousMode);
77
		
83
		
78
		actionInfo =
84
		LoopingFlowContext condLoopContext;
85
		FlowInfo condInfo =
79
			condition.analyseCode(
86
			condition.analyseCode(
80
				currentScope,
87
				currentScope,
81
				loopingContext,
88
				(condLoopContext =
89
					new LoopingFlowContext(flowContext,	flowInfo, this, null, 
90
						null, currentScope)),
82
				(action == null
91
				(action == null
83
					? actionInfo
92
					? actionInfo
84
					: (actionInfo.mergedWith(loopingContext.initsOnContinue))));
93
					: (actionInfo.mergedWith(loopingContext.initsOnContinue))).copy());
85
		if (!isConditionOptimizedFalse && continueLabel != null) {
94
		if (!isConditionOptimizedFalse && continueLabel != null) {
86
			loopingContext.complainOnDeferredChecks(currentScope, actionInfo);
95
			loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo);
96
			condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo);
97
			UnconditionalFlowInfo checkFlowInfo;
98
			loopingContext.complainOnDeferredNullChecks(currentScope, 
99
					checkFlowInfo = actionInfo.
100
						addPotentialNullInfoFrom(
101
						  condInfo.initsWhenTrue().unconditionalInits()));
102
			condLoopContext.complainOnDeferredNullChecks(currentScope, 
103
					checkFlowInfo);
87
		}
104
		}
88
105
89
		// end of loop
106
		// end of loop
90
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
107
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
91
				loopingContext.initsOnBreak, 
108
				loopingContext.initsOnBreak, 
92
				isConditionOptimizedTrue, 
109
				isConditionOptimizedTrue,
93
				actionInfo.initsWhenFalse().addInitializationsFrom(flowInfo), // recover null inits from before condition analysis
110
				condInfo.isReachable() ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse()) : condInfo, 
111
					// recover null inits from before condition analysis
94
				false, // never consider opt false case for DO loop, since break can always occur (47776)
112
				false, // never consider opt false case for DO loop, since break can always occur (47776)
95
				!isConditionTrue /*do{}while(true); unreachable(); */);
113
				!isConditionTrue /*do{}while(true); unreachable(); */);
96
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
114
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
(-)compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java (-28 / +42 lines)
Lines 22-105 Link Here
22
	public EqualExpression(Expression left, Expression right,int operator) {
22
	public EqualExpression(Expression left, Expression right,int operator) {
23
		super(left,right,operator);
23
		super(left,right,operator);
24
	}
24
	}
25
	public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
25
	private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
26
		
26
		
27
		LocalVariableBinding local = this.left.localVariableBinding();
27
		LocalVariableBinding local = this.left.localVariableBinding();
28
		if (local != null) {
28
		if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
29
			checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left);
29
			checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left);
30
		}
30
		}
31
		local = this.right.localVariableBinding();
31
		local = this.right.localVariableBinding();
32
		if (local != null) {
32
		if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
33
			checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right);
33
			checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right);
34
		}
34
		}
35
	}
35
	}
36
	private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
36
	private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
37
		switch (nullStatus) {
37
		switch (nullStatus) {
38
			case FlowInfo.NULL :
38
			case FlowInfo.NULL :
39
				flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NULL, flowInfo);
39
				flowContext.recordUsingNullReference(scope, local, reference, 
40
					FlowContext.CAN_ONLY_NULL_NON_NULL, flowInfo);
40
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
41
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
41
					initsWhenTrue.markAsDefinitelyNull(local); // from thereon it is set
42
					initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set
42
					initsWhenFalse.markAsDefinitelyNonNull(local); // from thereon it is set
43
					initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set
43
				} else {
44
				} else {
44
					initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set
45
					initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set
45
					initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set
46
					initsWhenFalse.markAsComparedEqualToNull(local); // from thereon it is set
46
				}
47
				}
47
				break;
48
				break;
48
			case FlowInfo.NON_NULL :
49
			case FlowInfo.NON_NULL :
49
				flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NON_NULL, flowInfo);
50
				flowContext.recordUsingNullReference(scope, local, reference, 
51
					FlowContext.CAN_ONLY_NULL, flowInfo);
50
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
52
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
51
					initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set
53
					initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set
52
				}
54
				}
53
				break;
55
				break;
54
		}	
56
		}	
55
	}
57
	}
56
	
58
	
57
	public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
59
	public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
60
		FlowInfo result;
58
		if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
61
		if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
59
			if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) {
62
			if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) {
60
				if (left.constant.booleanValue()) { //  true == anything
63
				if (left.constant.booleanValue()) { //  true == anything
61
					//  this is equivalent to the right argument inits 
64
					//  this is equivalent to the right argument inits 
62
					return right.analyseCode(currentScope, flowContext, flowInfo);
65
					result = right.analyseCode(currentScope, flowContext, flowInfo);
63
				} else { // false == anything
66
				} else { // false == anything
64
					//  this is equivalent to the right argument inits negated
67
					//  this is equivalent to the right argument inits negated
65
					return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
68
					result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
66
				}
69
				}
67
			}
70
			} 
68
			if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) {
71
			else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) {
69
				if (right.constant.booleanValue()) { //  anything == true
72
				if (right.constant.booleanValue()) { //  anything == true
70
					//  this is equivalent to the right argument inits 
73
					//  this is equivalent to the left argument inits 
71
					return left.analyseCode(currentScope, flowContext, flowInfo);
74
					result = left.analyseCode(currentScope, flowContext, flowInfo);
72
				} else { // anything == false
75
				} else { // anything == false
73
					//  this is equivalent to the right argument inits negated
76
					//  this is equivalent to the right argument inits negated
74
					return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
77
					result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
75
				}
78
				}
79
			} 
80
			else {
81
				result = right.analyseCode(
82
					currentScope, flowContext, 
83
					left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
76
			}
84
			}
77
			return right.analyseCode(
78
				currentScope, flowContext, 
79
				left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
80
		} else { //NOT_EQUAL :
85
		} else { //NOT_EQUAL :
81
			if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) {
86
			if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) {
82
				if (!left.constant.booleanValue()) { //  false != anything
87
				if (!left.constant.booleanValue()) { //  false != anything
83
					//  this is equivalent to the right argument inits 
88
					//  this is equivalent to the right argument inits 
84
					return right.analyseCode(currentScope, flowContext, flowInfo);
89
					result = right.analyseCode(currentScope, flowContext, flowInfo);
85
				} else { // true != anything
90
				} else { // true != anything
86
					//  this is equivalent to the right argument inits negated
91
					//  this is equivalent to the right argument inits negated
87
					return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
92
					result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
88
				}
93
				}
89
			}
94
			}
90
			if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) {
95
			else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) {
91
				if (!right.constant.booleanValue()) { //  anything != false
96
				if (!right.constant.booleanValue()) { //  anything != false
92
					//  this is equivalent to the right argument inits 
97
					//  this is equivalent to the right argument inits 
93
					return left.analyseCode(currentScope, flowContext, flowInfo);
98
					result = left.analyseCode(currentScope, flowContext, flowInfo);
94
				} else { // anything != true
99
				} else { // anything != true
95
					//  this is equivalent to the right argument inits negated
100
					//  this is equivalent to the right argument inits negated
96
					return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
101
					result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
97
				}
102
				}
103
			} 
104
			else {
105
				result = right.analyseCode(
106
					currentScope, flowContext, 
107
					left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).
108
					/* unneeded since we flatten it: asNegatedCondition(). */
109
					unconditionalInits();
98
			}
110
			}
99
			return right.analyseCode(
100
				currentScope, flowContext, 
101
				left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
102
		}
111
		}
112
		if (result instanceof UnconditionalFlowInfo && result.isReachable()) { // the flow info is flat
113
			result = FlowInfo.conditional(result, result.copy());
114
		}
115
	  checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse());
116
	  return result;
103
	}
117
	}
104
	
118
	
105
	public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
119
	public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
(-)compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java (-21 / +21 lines)
Lines 479-503 Link Here
479
		}
479
		}
480
	}	
480
	}	
481
	
481
	
482
	public FlowInfo checkNullStatus(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int nullStatus) {
482
/**
483
483
 * Check the local variable of this expression, if any, against potential NPEs 
484
		LocalVariableBinding local = this.localVariableBinding();
484
 * given a flow context and an upstream flow info. If so, report the risk to
485
		if (local != null) {
485
 * the context. Marks the local as checked, which affects the flow info.
486
			switch(nullStatus) {
486
 * @param scope the scope of the analysis
487
				case FlowInfo.NULL :
487
 * @param flowContext the current flow context
488
					flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NULL, flowInfo);
488
 * @param flowInfo the upstream flow info; caveat: may get modified
489
					flowInfo.markAsDefinitelyNull(local); // from thereon it is set
489
 * @param checkString if true, a local variable of type String is checked; else
490
					break;
490
 *        it is skipped
491
				case FlowInfo.NON_NULL :
491
 */
492
					flowContext.recordUsingNullReference(scope, local, this, FlowInfo.NON_NULL, flowInfo);
492
public void checkNPE(BlockScope scope, FlowContext flowContext, 
493
					flowInfo.markAsDefinitelyNonNull(local); // from thereon it is set
493
		FlowInfo flowInfo, boolean checkString) {
494
					break;
494
	LocalVariableBinding local = this.localVariableBinding();
495
				case FlowInfo.UNKNOWN :
495
	if (local != null && 
496
					break;
496
			(local.type.tagBits & TagBits.IsBaseType) == 0 &&
497
			}
497
			(checkString || local.type.id != T_JavaLangString)) {
498
		}
498
		flowContext.recordUsingNullReference(scope, local, this, 
499
		return flowInfo;
499
			FlowContext.MAY_NULL, flowInfo);
500
		flowInfo.markAsComparedEqualToNonNull(local); 
501
			// from thereon it is set
500
	}
502
	}
503
}
501
504
502
	private MethodBinding[] getAllInheritedMethods(ReferenceBinding binding) {
505
	private MethodBinding[] getAllInheritedMethods(ReferenceBinding binding) {
503
		ArrayList collector = new ArrayList();
506
		ArrayList collector = new ArrayList();
Lines 516-524 Link Here
516
			getAllInheritedMethods0(superInterfaces[i], collector);
519
			getAllInheritedMethods0(superInterfaces[i], collector);
517
		}
520
		}
518
	}
521
	}
519
	public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
520
		// do nothing by default - see EqualExpression
521
	}
522
522
523
	public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
523
	public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
524
		if (match == castType) {
524
		if (match == castType) {
(-)compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java (-1 / +7 lines)
Lines 98-104 Link Here
98
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
98
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
99
	boolean nonStatic = !binding.isStatic();
99
	boolean nonStatic = !binding.isStatic();
100
	receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic);
100
	receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic);
101
	if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
101
	if (nonStatic) {
102
		receiver.checkNPE(currentScope, flowContext, flowInfo, true);
103
	}
102
	
104
	
103
	if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
105
	if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
104
		manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
106
		manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
Lines 407-412 Link Here
407
	}		
409
	}		
408
}
410
}
409
411
412
public int nullStatus(FlowInfo flowInfo) {
413
	return FlowInfo.UNKNOWN;
414
}
415
410
public Constant optimizedBooleanConstant() {
416
public Constant optimizedBooleanConstant() {
411
	switch (this.resolvedType.id) {
417
	switch (this.resolvedType.id) {
412
		case T_boolean :
418
		case T_boolean :
(-)compiler/org/eclipse/jdt/internal/compiler/ast/ForStatement.java (-22 / +55 lines)
Lines 83-120 Link Here
83
		
83
		
84
		// process the condition
84
		// process the condition
85
		LoopingFlowContext condLoopContext = null;
85
		LoopingFlowContext condLoopContext = null;
86
		FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
86
		FlowInfo condInfo = flowInfo.nullInfoLessUnconditionalCopy();
87
		if (condition != null) {
87
		if (condition != null) {
88
			if (!isConditionTrue) {
88
			if (!isConditionTrue) {
89
				condInfo =
89
				condInfo =
90
					condition.analyseCode(
90
					condition.analyseCode(
91
						scope,
91
						scope,
92
						(condLoopContext =
92
						(condLoopContext =
93
							new LoopingFlowContext(flowContext, this, null, null, scope)),
93
							new LoopingFlowContext(flowContext, flowInfo, this, null, 
94
								null, scope)),
94
						condInfo);
95
						condInfo);
95
			}
96
			}
96
		}
97
		}
97
98
98
		// process the action
99
		// process the action
99
		LoopingFlowContext loopingContext;
100
		LoopingFlowContext loopingContext;
100
		FlowInfo actionInfo;
101
		UnconditionalFlowInfo actionInfo;
101
		if (action == null 
102
		if (action == null 
102
			|| (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
103
			|| (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
103
			if (condLoopContext != null)
104
			if (condLoopContext != null)
104
				condLoopContext.complainOnDeferredChecks(scope, condInfo);
105
				condLoopContext.complainOnDeferredFinalChecks(scope, condInfo);
105
			if (isConditionTrue) {
106
			if (isConditionTrue) {
107
				if (condLoopContext != null) {
108
					condLoopContext.complainOnDeferredNullChecks(currentScope,
109
						condInfo);
110
				}
106
				return FlowInfo.DEAD_END;
111
				return FlowInfo.DEAD_END;
107
			} else {
112
			} else {
108
				if (isConditionFalse){
113
				if (isConditionFalse){
109
					continueLabel = null; // for(;false;p());
114
					continueLabel = null; // for(;false;p());
110
				}
115
				}
111
				actionInfo = condInfo.initsWhenTrue().copy().unconditionalInits().discardNullRelatedInitializations();
116
				actionInfo = condInfo.initsWhenTrue().unconditionalCopy();
112
				loopingContext =
117
				loopingContext =
113
					new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
118
					new LoopingFlowContext(flowContext, flowInfo, this, 
119
						breakLabel, continueLabel, scope);
114
			}
120
			}
115
		} else {
121
		} 
122
		else {
116
			loopingContext =
123
			loopingContext =
117
				new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
124
				new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, 
125
					continueLabel, scope);
118
			FlowInfo initsWhenTrue = condInfo.initsWhenTrue();
126
			FlowInfo initsWhenTrue = condInfo.initsWhenTrue();
119
			condIfTrueInitStateIndex =
127
			condIfTrueInitStateIndex =
120
				currentScope.methodScope().recordInitializationStates(initsWhenTrue);
128
				currentScope.methodScope().recordInitializationStates(initsWhenTrue);
Lines 122-159 Link Here
122
				if (isConditionFalse) {
130
				if (isConditionFalse) {
123
					actionInfo = FlowInfo.DEAD_END;
131
					actionInfo = FlowInfo.DEAD_END;
124
				} else {
132
				} else {
125
					actionInfo = initsWhenTrue.copy().unconditionalInits().discardNullRelatedInitializations();
133
					actionInfo = initsWhenTrue.unconditionalCopy();
126
					if (isConditionOptimizedFalse){
134
					if (isConditionOptimizedFalse){
127
						actionInfo.setReachMode(FlowInfo.UNREACHABLE);
135
						actionInfo.setReachMode(FlowInfo.UNREACHABLE);
128
					}
136
					}
129
				}
137
				}
130
			if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
138
			if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
131
				actionInfo = action.analyseCode(scope, loopingContext, actionInfo);
139
				actionInfo = action.analyseCode(scope, loopingContext, actionInfo).
140
					unconditionalInits();
132
			}
141
			}
133
142
134
			// code generation can be optimized when no need to continue in the loop
143
			// code generation can be optimized when no need to continue in the loop
135
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
144
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
136
				continueLabel = null;
145
				continueLabel = null;
137
			} else {
146
			} 
138
				if (condLoopContext != null)
147
			else {
139
					condLoopContext.complainOnDeferredChecks(scope, condInfo);
148
				if (condLoopContext != null) {
140
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
149
					condLoopContext.complainOnDeferredFinalChecks(scope, 
141
				loopingContext.complainOnDeferredChecks(scope, actionInfo);
150
							condInfo);
151
				}
152
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue);
153
				loopingContext.complainOnDeferredFinalChecks(scope, 
154
						actionInfo);
142
			}
155
			}
143
		}
156
		}
144
		// for increments
157
		// for increments
145
		FlowInfo exitBranch = condInfo.initsWhenFalse();
158
		FlowInfo exitBranch = flowInfo.copy();
146
		exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis
159
		// recover null inits from before condition analysis
160
		LoopingFlowContext incrementContext = null;
147
		if (continueLabel != null) {
161
		if (continueLabel != null) {
148
			if (increments != null) {
162
			if (increments != null) {
149
				LoopingFlowContext loopContext =
163
				incrementContext =
150
					new LoopingFlowContext(flowContext, this, null, null, scope);
164
					new LoopingFlowContext(flowContext, flowInfo, this, null, 
165
						null, scope);
166
				FlowInfo incrementInfo = actionInfo;
151
				for (int i = 0, count = increments.length; i < count; i++) {
167
				for (int i = 0, count = increments.length; i < count; i++) {
152
					actionInfo = increments[i].analyseCode(scope, loopContext, actionInfo);
168
					incrementInfo = increments[i].
169
						analyseCode(scope, incrementContext, incrementInfo);
153
				}
170
				}
154
				loopContext.complainOnDeferredChecks(scope, actionInfo);
171
				incrementContext.complainOnDeferredFinalChecks(scope,
172
						actionInfo = incrementInfo.unconditionalInits());
155
			}
173
			}
156
			exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
174
			exitBranch.addPotentialInitializationsFrom(actionInfo).
175
				addInitializationsFrom(condInfo.initsWhenFalse());
176
		}
177
		else {
178
			exitBranch.addInitializationsFrom(condInfo.initsWhenFalse());
179
		}
180
		// nulls checks
181
		if (condLoopContext != null) {
182
			condLoopContext.complainOnDeferredNullChecks(currentScope, 
183
				actionInfo);
184
		}
185
		loopingContext.complainOnDeferredNullChecks(currentScope, 
186
			actionInfo);
187
		if (incrementContext != null) {
188
			incrementContext.complainOnDeferredNullChecks(currentScope, 
189
				actionInfo);
157
		}
190
		}
158
191
159
		//end of loop
192
		//end of loop
(-)compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java (-10 / +18 lines)
Lines 17-22 Link Here
17
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
17
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
18
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
18
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
19
import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext;
19
import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext;
20
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
20
import org.eclipse.jdt.internal.compiler.impl.Constant;
21
import org.eclipse.jdt.internal.compiler.impl.Constant;
21
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
22
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
22
import org.eclipse.jdt.internal.compiler.lookup.Binding;
23
import org.eclipse.jdt.internal.compiler.lookup.Binding;
Lines 81-89 Link Here
81
		continueLabel = new Label();
82
		continueLabel = new Label();
82
83
83
		// process the element variable and collection
84
		// process the element variable and collection
85
		this.collection.checkNPE(currentScope, flowContext, flowInfo, true);
84
		flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
86
		flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
85
		FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
87
		FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy());
86
		condInfo = this.collection.analyseCode(scope, flowContext, condInfo);
87
88
88
		// element variable will be assigned when iterating
89
		// element variable will be assigned when iterating
89
		condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
90
		condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
Lines 91-115 Link Here
91
		this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
92
		this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
92
		
93
		
93
		// process the action
94
		// process the action
94
		LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
95
		LoopingFlowContext loopingContext = 
95
		FlowInfo actionInfo = condInfo.initsWhenTrue().copy();
96
			new LoopingFlowContext(flowContext, flowInfo, this, breakLabel, 
97
				continueLabel, scope);
98
		UnconditionalFlowInfo actionInfo = 
99
			condInfo.nullInfoLessUnconditionalCopy();
96
		FlowInfo exitBranch;
100
		FlowInfo exitBranch;
97
		if (!(action == null || (action.isEmptyBlock() 
101
		if (!(action == null || (action.isEmptyBlock() 
98
		        	&& currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) {
102
		        	&& currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) {
99
103
100
			if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
104
			if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
101
				actionInfo = action.analyseCode(scope, loopingContext, actionInfo);
105
				actionInfo = action.
106
					analyseCode(scope, loopingContext, actionInfo).
107
					unconditionalCopy();
102
			}
108
			}
103
109
104
			// code generation can be optimized when no need to continue in the loop
110
			// code generation can be optimized when no need to continue in the loop
105
			exitBranch = condInfo.initsWhenFalse();
111
			exitBranch = flowInfo.unconditionalCopy().
106
			exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis			
112
				addInitializationsFrom(condInfo.initsWhenFalse()); 
107
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
113
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
108
				continueLabel = null;
114
				continueLabel = null;
109
			} else {
115
			} else {
110
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
116
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue);
111
				loopingContext.complainOnDeferredChecks(scope, actionInfo);
117
				loopingContext.complainOnDeferredFinalChecks(scope, actionInfo);
112
				exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
118
				exitBranch.addPotentialInitializationsFrom(actionInfo);
113
			}
119
			}
114
		} else {
120
		} else {
115
			exitBranch = condInfo.initsWhenFalse();
121
			exitBranch = condInfo.initsWhenFalse();
Lines 133-138 Link Here
133
			}
139
			}
134
		}
140
		}
135
		//end of loop
141
		//end of loop
142
		loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo);
143
136
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
144
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
137
				loopingContext.initsOnBreak, 
145
				loopingContext.initsOnBreak, 
138
				false, 
146
				false, 
(-)compiler/org/eclipse/jdt/internal/compiler/ast/IfStatement.java (-9 / +9 lines)
Lines 60-81 Link Here
60
		FlowInfo flowInfo) {
60
		FlowInfo flowInfo) {
61
61
62
		// process the condition
62
		// process the condition
63
		flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo);
63
		FlowInfo conditionFlowInfo =
64
			condition.analyseCode(currentScope, flowContext, flowInfo);
64
65
65
		Constant cst = this.condition.optimizedBooleanConstant();
66
		Constant cst = this.condition.optimizedBooleanConstant();
66
		boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
67
		boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
67
		boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
68
		boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
68
		
69
		
69
		// process the THEN part
70
		// process the THEN part
70
		FlowInfo thenFlowInfo = flowInfo.initsWhenTrue().copy();
71
		FlowInfo thenFlowInfo = conditionFlowInfo.safeInitsWhenTrue();
71
		if (isConditionOptimizedFalse) {
72
		if (isConditionOptimizedFalse) {
72
			thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
73
			thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
73
		}
74
		}
74
		FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy();
75
		FlowInfo elseFlowInfo = conditionFlowInfo.initsWhenFalse();
75
		if (isConditionOptimizedTrue) {
76
		if (isConditionOptimizedTrue) {
76
			elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
77
			elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); 
77
		}
78
		}
78
		this.condition.checkNullComparison(currentScope, flowContext, flowInfo, thenFlowInfo, elseFlowInfo);
79
		if (this.thenStatement != null) {
79
		if (this.thenStatement != null) {
80
			// Save info for code gen
80
			// Save info for code gen
81
			thenInitStateIndex =
81
			thenInitStateIndex =
Lines 107-117 Link Here
107
107
108
		// merge THEN & ELSE initializations
108
		// merge THEN & ELSE initializations
109
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
109
		FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
110
				thenFlowInfo, 
110
			thenFlowInfo, 
111
				isConditionOptimizedTrue, 
111
			isConditionOptimizedTrue, 
112
				elseFlowInfo, 
112
			elseFlowInfo, 
113
				isConditionOptimizedFalse,
113
			isConditionOptimizedFalse,
114
				true /*if(true){ return; }  fake-reachable(); */);
114
			true /*if(true){ return; }  fake-reachable(); */);
115
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
115
		mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
116
		return mergedInfo;
116
		return mergedInfo;
117
	}
117
	}
(-)compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java (-7 / +8 lines)
Lines 31-47 Link Here
31
		this.sourceEnd = type.sourceEnd;
31
		this.sourceEnd = type.sourceEnd;
32
	}
32
	}
33
33
34
	public FlowInfo analyseCode(
34
public FlowInfo analyseCode(
35
		BlockScope currentScope,
35
		BlockScope currentScope,
36
		FlowContext flowContext,
36
		FlowContext flowContext,
37
		FlowInfo flowInfo) {
37
		FlowInfo flowInfo) {
38
38
	LocalVariableBinding local = this.expression.localVariableBinding();
39
		flowInfo = expression
39
	if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
40
			.analyseCode(currentScope, flowContext, flowInfo)
40
		flowContext.recordUsingNullReference(currentScope, local, 
41
			.unconditionalInits();
41
			this.expression, FlowContext.CAN_ONLY_NULL, flowInfo);
42
		expression.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
43
		return flowInfo;
44
	}
42
	}
43
	return expression.analyseCode(currentScope, flowContext, flowInfo).
44
		unconditionalInits();
45
}
45
46
46
	/**
47
	/**
47
	 * Code generation for instanceOfExpression
48
	 * Code generation for instanceOfExpression
(-)compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java (-19 / +19 lines)
Lines 32-56 Link Here
32
		this.declarationEnd = sourceEnd;
32
		this.declarationEnd = sourceEnd;
33
	}
33
	}
34
34
35
	public FlowInfo analyseCode(
35
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
36
		BlockScope currentScope,
37
		FlowContext flowContext,
38
		FlowInfo flowInfo) {
36
		FlowInfo flowInfo) {
39
37
	// record variable initialization if any
40
		// record variable initialization if any
38
	if (flowInfo.isReachable()) {
41
		if (flowInfo.isReachable()) {
39
		bits |= IsLocalDeclarationReachable; // only set if actually reached
42
			bits |= IsLocalDeclarationReachable; // only set if actually reached
40
	}
43
		}
41
	if (this.initialization == null) { 
44
		if (this.initialization == null) 
42
		return flowInfo;
45
			return flowInfo;
43
	}
46
			
44
	int nullStatus = this.initialization.nullStatus(flowInfo);
47
		int nullStatus = this.initialization.nullStatus(flowInfo);
45
	flowInfo =
48
		flowInfo =
46
		this.initialization
49
			this.initialization
47
			.analyseCode(currentScope, flowContext, flowInfo)
50
				.analyseCode(currentScope, flowContext, flowInfo)
48
			.unconditionalInits();
51
				.unconditionalInits();
49
	flowInfo.markAsDefinitelyAssigned(binding);
52
		
50
	if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) {
53
		flowInfo.markAsDefinitelyAssigned(binding);
54
		switch(nullStatus) {
51
		switch(nullStatus) {
55
			case FlowInfo.NULL :
52
			case FlowInfo.NULL :
56
				flowInfo.markAsDefinitelyNull(this.binding);
53
				flowInfo.markAsDefinitelyNull(this.binding);
Lines 58-66 Link Here
58
			case FlowInfo.NON_NULL :
55
			case FlowInfo.NON_NULL :
59
				flowInfo.markAsDefinitelyNonNull(this.binding);
56
				flowInfo.markAsDefinitelyNonNull(this.binding);
60
				break;
57
				break;
58
			default:
59
				flowInfo.markAsDefinitelyUnknown(this.binding);
61
		}
60
		}
62
		return flowInfo;
63
	}
61
	}
62
	return flowInfo;
63
}
64
64
65
	public void checkModifiers() {
65
	public void checkModifiers() {
66
66
(-)compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java (-2 / +7 lines)
Lines 42-48 Link Here
42
42
43
	boolean nonStatic = !binding.isStatic();
43
	boolean nonStatic = !binding.isStatic();
44
	flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits();
44
	flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic).unconditionalInits();
45
	if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
45
	if (nonStatic) {
46
		receiver.checkNPE(currentScope, flowContext, flowInfo, true);
47
	}
46
48
47
	if (arguments != null) {
49
	if (arguments != null) {
48
		int length = arguments.length;
50
		int length = arguments.length;
Lines 53-59 Link Here
53
	ReferenceBinding[] thrownExceptions;
55
	ReferenceBinding[] thrownExceptions;
54
	if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
56
	if ((thrownExceptions = binding.thrownExceptions) != NoExceptions) {
55
		// must verify that exceptions potentially thrown by this expression are caught in the method
57
		// must verify that exceptions potentially thrown by this expression are caught in the method
56
		flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope);
58
		flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope);
59
		// TODO (maxime) the copy above is needed because of a side effect into 
60
		//               checkExceptionHandlers; consider protecting there instead of here;
61
		//               NullReferenceTest#test0510
57
	}
62
	}
58
	manageSyntheticAccessIfNecessary(currentScope, flowInfo);	
63
	manageSyntheticAccessIfNecessary(currentScope, flowInfo);	
59
	return flowInfo;
64
	return flowInfo;
(-)compiler/org/eclipse/jdt/internal/compiler/ast/OR_OR_Expression.java (-7 / +4 lines)
Lines 50-56 Link Here
50
	
50
	
51
		 // need to be careful of scenario:
51
		 // need to be careful of scenario:
52
		//		(x || y) || !z, if passing the left info to the right, it would be swapped by the !
52
		//		(x || y) || !z, if passing the left info to the right, it would be swapped by the !
53
		FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalInits().copy();
53
		FlowInfo rightInfo = leftInfo.initsWhenFalse().unconditionalCopy();
54
		rightInitStateIndex =
54
		rightInitStateIndex =
55
			currentScope.methodScope().recordInitializationStates(rightInfo);
55
			currentScope.methodScope().recordInitializationStates(rightInfo);
56
56
Lines 59-72 Link Here
59
			rightInfo.setReachMode(FlowInfo.UNREACHABLE); 
59
			rightInfo.setReachMode(FlowInfo.UNREACHABLE); 
60
		}
60
		}
61
		rightInfo = right.analyseCode(currentScope, flowContext, rightInfo);
61
		rightInfo = right.analyseCode(currentScope, flowContext, rightInfo);
62
		FlowInfo falseMergedInfo = rightInfo.initsWhenFalse().copy();
63
		rightInfo.setReachMode(previousMode); // reset after falseMergedInfo got extracted
64
65
		FlowInfo mergedInfo = FlowInfo.conditional(
62
		FlowInfo mergedInfo = FlowInfo.conditional(
66
					// merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // b may not have been initialized
63
					// merging two true initInfos for such a negative case: if ((t && (b = t)) || f) r = b; // b may not have been initialized
67
					leftInfo.initsWhenTrue().copy().unconditionalInits().mergedWith(
64
					leftInfo.initsWhenTrue().unconditionalInits().mergedWith(
68
						rightInfo.initsWhenTrue().copy().unconditionalInits()),
65
						rightInfo.safeInitsWhenTrue().setReachMode(previousMode).unconditionalInits()),
69
					falseMergedInfo);
66
					rightInfo.initsWhenFalse());
70
		mergedInitStateIndex =
67
		mergedInitStateIndex =
71
			currentScope.methodScope().recordInitializationStates(mergedInfo);
68
			currentScope.methodScope().recordInitializationStates(mergedInfo);
72
		return mergedInfo;
69
		return mergedInfo;
(-)compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java (-3 / +24 lines)
Lines 98-104 Link Here
98
				} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
98
				} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
99
					localBinding.useFlag = LocalVariableBinding.FAKE_USED;
99
					localBinding.useFlag = LocalVariableBinding.FAKE_USED;
100
				}
100
				}
101
				this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
101
				checkNPE(currentScope, flowContext, flowInfo, true);
102
		}
102
		}
103
		
103
		
104
		if (needValue) {
104
		if (needValue) {
Lines 257-263 Link Here
257
				} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
257
				} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
258
					localBinding.useFlag = LocalVariableBinding.FAKE_USED;
258
					localBinding.useFlag = LocalVariableBinding.FAKE_USED;
259
				}
259
				}
260
				this.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
260
				checkNPE(currentScope, flowContext, flowInfo, true);
261
		}
261
		}
262
		if (needValue) {
262
		if (needValue) {
263
			manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
263
			manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
Lines 304-310 Link Here
304
		bits |= Binding.FIELD;
304
		bits |= Binding.FIELD;
305
		return getOtherFieldBindings(scope);
305
		return getOtherFieldBindings(scope);
306
	}
306
	}
307
	
307
308
public void checkNPE(BlockScope scope, FlowContext flowContext, 
309
		FlowInfo flowInfo, boolean checkString) {
310
	// cannot override localVariableBinding because this would project o.m onto o when
311
	// analysing assignments
312
	if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) {
313
		LocalVariableBinding local = (LocalVariableBinding) this.binding;
314
		if (local != null && 
315
			(local.type.tagBits & TagBits.IsBaseType) == 0 &&
316
			(checkString || local.type.id != T_JavaLangString)) {
317
			flowContext.recordUsingNullReference(scope, local, this, 
318
				FlowContext.MAY_NULL, flowInfo);
319
			flowInfo.markAsComparedEqualToNonNull(local); 
320
				// from thereon it is set
321
		}
322
	}
323
}
308
	/**
324
	/**
309
	 * @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)
325
	 * @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)
310
	 */
326
	 */
Lines 773-778 Link Here
773
				? type.capture(scope, this.sourceEnd)
789
				? type.capture(scope, this.sourceEnd)
774
				: type;		
790
				: type;		
775
	}
791
	}
792
776
	public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
793
	public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
777
		if (!flowInfo.isReachable()) return;
794
		if (!flowInfo.isReachable()) return;
778
		//If inlinable field, forget the access emulation, the code gen will directly target it
795
		//If inlinable field, forget the access emulation, the code gen will directly target it
Lines 849-854 Link Here
849
		}			
866
		}			
850
	}
867
	}
851
868
869
public int nullStatus(FlowInfo flowInfo) {
870
	return FlowInfo.UNKNOWN;
871
}
872
852
	public Constant optimizedBooleanConstant() {
873
	public Constant optimizedBooleanConstant() {
853
874
854
		switch (this.resolvedType.id) {
875
		switch (this.resolvedType.id) {
(-)compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java (-16 lines)
Lines 68-87 Link Here
68
public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired);
68
public abstract void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired);
69
69
70
public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired);
70
public abstract void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired);
71
72
public int nullStatus(FlowInfo flowInfo) {
73
74
	if (this.constant != null && this.constant != Constant.NotAConstant)
75
		return FlowInfo.NON_NULL; // constant expression cannot be null
76
	
77
	LocalVariableBinding local = localVariableBinding();
78
	if (local != null) {
79
		if (flowInfo.isDefinitelyNull(local))
80
			return FlowInfo.NULL;
81
		if (flowInfo.isDefinitelyNonNull(local))
82
			return FlowInfo.NON_NULL;
83
	}	
84
	return FlowInfo.UNKNOWN;
85
}
86
87
}
71
}
(-)compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java (+21 lines)
Lines 676-681 Link Here
676
			}					
676
			}					
677
		}
677
		}
678
	}
678
	}
679
680
public int nullStatus(FlowInfo flowInfo) {
681
	if (this.constant != null && this.constant != Constant.NotAConstant) {
682
		return FlowInfo.NON_NULL; // constant expression cannot be null
683
	}
684
	switch (bits & RestrictiveFlagMASK) {
685
		case Binding.FIELD : // reading a field
686
			return FlowInfo.UNKNOWN;
687
		case Binding.LOCAL : // reading a local variable
688
			LocalVariableBinding local = (LocalVariableBinding) this.binding;
689
			if (local != null) {
690
				if (flowInfo.isDefinitelyNull(local))
691
					return FlowInfo.NULL;
692
				if (flowInfo.isDefinitelyNonNull(local))
693
					return FlowInfo.NON_NULL;
694
				return FlowInfo.UNKNOWN;
695
			}
696
	}
697
	return FlowInfo.NON_NULL; 
698
}
699
679
	public StringBuffer printExpression(int indent, StringBuffer output){
700
	public StringBuffer printExpression(int indent, StringBuffer output){
680
	
701
	
681
		return output.append(token);
702
		return output.append(token);
(-)compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java (-2 / +2 lines)
Lines 62-72 Link Here
62
					if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case
62
					if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case
63
						this.scope.enclosingCase = cases[caseIndex]; // record entering in a switch case block
63
						this.scope.enclosingCase = cases[caseIndex]; // record entering in a switch case block
64
						caseIndex++;
64
						caseIndex++;
65
						caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
65
						caseInits = caseInits.mergedWith(flowInfo.unconditionalInits());
66
						didAlreadyComplain = false; // reset complaint
66
						didAlreadyComplain = false; // reset complaint
67
					} else if (statement == defaultCase) { // statement is the default case
67
					} else if (statement == defaultCase) { // statement is the default case
68
						this.scope.enclosingCase = defaultCase; // record entering in a switch case block
68
						this.scope.enclosingCase = defaultCase; // record entering in a switch case block
69
						caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
69
						caseInits = caseInits.mergedWith(flowInfo.unconditionalInits());
70
						didAlreadyComplain = false; // reset complaint
70
						didAlreadyComplain = false; // reset complaint
71
					}
71
					}
72
					if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) {
72
					if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) {
(-)compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java (-10 / +13 lines)
Lines 85-91 Link Here
85
					.analyseCode(
85
					.analyseCode(
86
						currentScope,
86
						currentScope,
87
						finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
87
						finallyContext = new FinallyFlowContext(flowContext, finallyBlock),
88
						flowInfo.copy().unconditionalInits().discardNullRelatedInitializations())
88
						flowInfo.nullInfoLessUnconditionalCopy())
89
					.unconditionalInits();
89
					.unconditionalInits();
90
			if (subInfo == FlowInfo.DEAD_END) {
90
			if (subInfo == FlowInfo.DEAD_END) {
91
				isSubRoutineEscaping = true;
91
				isSubRoutineEscaping = true;
Lines 121-133 Link Here
121
			for (int i = 0; i < catchCount; i++) {
121
			for (int i = 0; i < catchCount; i++) {
122
				// keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
122
				// keep track of the inits that could potentially have led to this exception handler (for final assignments diagnosis)
123
				FlowInfo catchInfo =
123
				FlowInfo catchInfo =
124
					flowInfo
124
					flowInfo.unconditionalCopy().
125
						.copy()
125
						addPotentialInitializationsFrom(
126
						.unconditionalInits()
126
							handlingContext.initsOnException(
127
								caughtExceptionTypes[i]))
127
						.addPotentialInitializationsFrom(
128
						.addPotentialInitializationsFrom(
128
							handlingContext.initsOnException(caughtExceptionTypes[i]).unconditionalInits())
129
							tryInfo.unconditionalInits())
129
						.addPotentialInitializationsFrom(tryInfo.unconditionalInits())
130
						.addPotentialInitializationsFrom(
130
						.addPotentialInitializationsFrom(handlingContext.initsOnReturn);
131
							handlingContext.initsOnReturn);
132
				// WORK this one is wrong -- too permissive for test case NullReferenceTest#test0560
131
133
132
				// catch var is always set
134
				// catch var is always set
133
				LocalVariableBinding catchArg = catchArguments[i].binding;
135
				LocalVariableBinding catchArg = catchArguments[i].binding;
Lines 162-171 Link Here
162
164
163
		// we also need to check potential multiple assignments of final variables inside the finally block
165
		// we also need to check potential multiple assignments of final variables inside the finally block
164
		// need to include potential inits from returns inside the try/catch parts - 1GK2AOF
166
		// need to include potential inits from returns inside the try/catch parts - 1GK2AOF
165
		finallyContext.complainOnDeferredChecks(
167
		finallyContext.complainOnDeferredChecks( //$NON-NULL-1$ null with subRoutineStartLabel, which returns
166
			tryInfo.isReachable() 
168
			tryInfo.isReachable() 
167
				? (tryInfo.addPotentialInitializationsFrom(insideSubContext.initsOnReturn))
169
				? (tryInfo.addPotentialInitializationsFrom(
168
				: insideSubContext.initsOnReturn, 
170
					insideSubContext.initsOnReturn)) //$NON-NULL-1$ null with subRoutineStartLabel, which returns
171
				: insideSubContext.initsOnReturn, //$NON-NULL-1$ null with subRoutineStartLabel, which returns
169
			currentScope);
172
			currentScope);
170
		if (subInfo == FlowInfo.DEAD_END) {
173
		if (subInfo == FlowInfo.DEAD_END) {
171
			mergedInitStateIndex =
174
			mergedInitStateIndex =
(-)compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java (-4 / +4 lines)
Lines 634-640 Link Here
634
	 *	Common flow analysis for all types
634
	 *	Common flow analysis for all types
635
	 *
635
	 *
636
	 */
636
	 */
637
	public void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) {
637
	private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) {
638
638
639
		if ((this.binding.isPrivate()/* || (this.binding.tagBits & (TagBits.IsAnonymousType|TagBits.IsLocalType)) == TagBits.IsLocalType*/) && !this.binding.isUsed()) {
639
		if ((this.binding.isPrivate()/* || (this.binding.tagBits & (TagBits.IsAnonymousType|TagBits.IsLocalType)) == TagBits.IsLocalType*/) && !this.binding.isUsed()) {
640
			if (!scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
640
			if (!scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
Lines 644-651 Link Here
644
644
645
		InitializationFlowContext initializerContext = new InitializationFlowContext(null, this, initializerScope);
645
		InitializationFlowContext initializerContext = new InitializationFlowContext(null, this, initializerScope);
646
		InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, staticInitializerScope);
646
		InitializationFlowContext staticInitializerContext = new InitializationFlowContext(null, this, staticInitializerScope);
647
		FlowInfo nonStaticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
647
		FlowInfo nonStaticFieldInfo = flowInfo.unconditionalFieldLessCopy();
648
		FlowInfo staticFieldInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
648
		FlowInfo staticFieldInfo = flowInfo.unconditionalFieldLessCopy();
649
		if (fields != null) {
649
		if (fields != null) {
650
			for (int i = 0, count = fields.length; i < count; i++) {
650
			for (int i = 0, count = fields.length; i < count; i++) {
651
				FieldDeclaration field = fields[i];
651
				FieldDeclaration field = fields[i];
Lines 699-705 Link Here
699
			}
699
			}
700
		}
700
		}
701
		if (methods != null) {
701
		if (methods != null) {
702
			UnconditionalFlowInfo outerInfo = flowInfo.copy().unconditionalInits().discardFieldInitializations();
702
			UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy();
703
			FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
703
			FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
704
			for (int i = 0, count = methods.length; i < count; i++) {
704
			for (int i = 0, count = methods.length; i < count; i++) {
705
				AbstractMethodDeclaration method = methods[i];
705
				AbstractMethodDeclaration method = methods[i];
(-)compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java (-9 / +10 lines)
Lines 27-45 Link Here
27
		this.bits |= operator << OperatorSHIFT; // encode operator
27
		this.bits |= operator << OperatorSHIFT; // encode operator
28
	}
28
	}
29
29
30
	public FlowInfo analyseCode(
30
public FlowInfo analyseCode(
31
		BlockScope currentScope,
31
		BlockScope currentScope,
32
		FlowContext flowContext,
32
		FlowContext flowContext,
33
		FlowInfo flowInfo) {
33
		FlowInfo flowInfo) {
34
			
34
	this.expression.checkNPE(currentScope, flowContext, flowInfo, true);	
35
		if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
35
	if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
36
			return this.expression
36
		return this.expression.
37
				.analyseCode(currentScope, flowContext, flowInfo)
37
			analyseCode(currentScope, flowContext, flowInfo).
38
				.asNegatedCondition();
38
			asNegatedCondition();
39
		} else {
39
	} else {
40
			return this.expression.analyseCode(currentScope, flowContext, flowInfo);
40
		return this.expression.
41
		}
41
			analyseCode(currentScope, flowContext, flowInfo);
42
	}
42
	}
43
}
43
44
44
	public Constant optimizedBooleanConstant() {
45
	public Constant optimizedBooleanConstant() {
45
		
46
		
(-)compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java (-10 / +31 lines)
Lines 55-65 Link Here
55
		preCondInitStateIndex =
55
		preCondInitStateIndex =
56
			currentScope.methodScope().recordInitializationStates(flowInfo);
56
			currentScope.methodScope().recordInitializationStates(flowInfo);
57
		LoopingFlowContext condLoopContext;
57
		LoopingFlowContext condLoopContext;
58
		FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
58
		FlowInfo condInfo =	flowInfo.nullInfoLessUnconditionalCopy();
59
		// we need to collect the contribution to nulls of the coming paths through the
60
		// loop, be they falling through normally or branched to break, continue labels
61
		// or catch blocks
59
		condInfo = this.condition.analyseCode(
62
		condInfo = this.condition.analyseCode(
60
				currentScope,
63
				currentScope,
61
				(condLoopContext =
64
				(condLoopContext =
62
					new LoopingFlowContext(flowContext, this, null, null, currentScope)),
65
					new LoopingFlowContext(flowContext, flowInfo, this, null, 
66
						null, currentScope)),
63
				condInfo);
67
				condInfo);
64
68
65
		LoopingFlowContext loopingContext;
69
		LoopingFlowContext loopingContext;
Lines 67-83 Link Here
67
		FlowInfo exitBranch;
71
		FlowInfo exitBranch;
68
		if (action == null 
72
		if (action == null 
69
			|| (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
73
			|| (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
70
			condLoopContext.complainOnDeferredChecks(currentScope, condInfo);
74
			condLoopContext.complainOnDeferredFinalChecks(currentScope, 
75
					condInfo);
76
			condLoopContext.complainOnDeferredNullChecks(currentScope,
77
				condInfo.unconditionalInits());
71
			if (isConditionTrue) {
78
			if (isConditionTrue) {
72
				return FlowInfo.DEAD_END;
79
				return FlowInfo.DEAD_END;
73
			} else {
80
			} else {
74
				FlowInfo mergedInfo = condInfo.initsWhenFalse().unconditionalInits();
81
				FlowInfo mergedInfo = condInfo.initsWhenFalse();
75
				if (isConditionOptimizedTrue){
82
				if (isConditionOptimizedTrue){
76
					mergedInfo.setReachMode(FlowInfo.UNREACHABLE);
83
					mergedInfo.setReachMode(FlowInfo.UNREACHABLE);
77
				}
84
				}
78
				mergedInitStateIndex =
85
				mergedInitStateIndex =
79
					currentScope.methodScope().recordInitializationStates(mergedInfo);
86
					currentScope.methodScope().recordInitializationStates(mergedInfo);
80
				return mergedInfo;
87
				return flowInfo.unconditionalInits().
88
					addPotentialNullInfoFrom(
89
							condInfo.initsWhenFalse().unconditionalInits()); 
81
			}
90
			}
82
		} else {
91
		} else {
83
			// in case the condition was inlined to false, record the fact that there is no way to reach any 
92
			// in case the condition was inlined to false, record the fact that there is no way to reach any 
Lines 85-90 Link Here
85
			loopingContext =
94
			loopingContext =
86
				new LoopingFlowContext(
95
				new LoopingFlowContext(
87
					flowContext,
96
					flowContext,
97
					flowInfo,
88
					this,
98
					this,
89
					breakLabel,
99
					breakLabel,
90
					continueLabel,
100
					continueLabel,
Lines 108-122 Link Here
108
			}
118
			}
109
119
110
			// code generation can be optimized when no need to continue in the loop
120
			// code generation can be optimized when no need to continue in the loop
111
			exitBranch = condInfo.initsWhenFalse();
121
			exitBranch = flowInfo.copy();
112
			exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis
122
			// need to start over from flowInfo so as to get null inits
123
113
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
124
			if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
114
				continueLabel = null;
125
				continueLabel = null;
126
				exitBranch.addInitializationsFrom(condInfo.initsWhenFalse());
115
			} else {
127
			} else {
116
				condLoopContext.complainOnDeferredChecks(currentScope, condInfo);
128
				condLoopContext.complainOnDeferredFinalChecks(currentScope, 
129
						condInfo);
117
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
130
				actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
118
				loopingContext.complainOnDeferredChecks(currentScope, actionInfo);
131
				condLoopContext.complainOnDeferredNullChecks(currentScope, 
119
				exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
132
						actionInfo);
133
				loopingContext.complainOnDeferredFinalChecks(currentScope, 
134
						actionInfo);
135
				loopingContext.complainOnDeferredNullChecks(currentScope, 
136
						actionInfo);
137
				exitBranch.
138
					addPotentialInitializationsFrom(
139
						actionInfo.unconditionalInits()).
140
					addInitializationsFrom(condInfo.initsWhenFalse());
120
			}
141
			}
121
		}
142
		}
122
143
(-)compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java (-85 / +93 lines)
Lines 83-134 Link Here
83
				&& initsWhenFalse.isDefinitelyAssigned(local);
83
				&& initsWhenFalse.isDefinitelyAssigned(local);
84
	}
84
	}
85
	
85
	
86
	/**
86
public boolean isDefinitelyNonNull(LocalVariableBinding local) {
87
	 * Check status of definite non-null assignment for a field.
87
	return initsWhenTrue.isDefinitelyNonNull(local) 
88
	 */
88
			&& initsWhenFalse.isDefinitelyNonNull(local);
89
	public boolean isDefinitelyNonNull(FieldBinding field) {
89
}
90
		
91
		return initsWhenTrue.isDefinitelyNonNull(field) 
92
				&& initsWhenFalse.isDefinitelyNonNull(field);
93
	}
94
95
	/**
96
	 * Check status of definite non-null assignment for a local variable.
97
	 */
98
	public boolean isDefinitelyNonNull(LocalVariableBinding local) {
99
		
100
		return initsWhenTrue.isDefinitelyNonNull(local) 
101
				&& initsWhenFalse.isDefinitelyNonNull(local);
102
	}
103
	
90
	
104
	/**
91
public boolean isDefinitelyNull(LocalVariableBinding local) {
105
	 * Check status of definite null assignment for a field.
92
	return initsWhenTrue.isDefinitelyNull(local) 
106
	 */
93
			&& initsWhenFalse.isDefinitelyNull(local);
107
	public boolean isDefinitelyNull(FieldBinding field) {
94
}
108
		
109
		return initsWhenTrue.isDefinitelyNull(field) 
110
				&& initsWhenFalse.isDefinitelyNull(field);
111
	}
112
113
	/**
114
	 * Check status of definite null assignment for a local variable.
115
	 */
116
	public boolean isDefinitelyNull(LocalVariableBinding local) {
117
		
118
		return initsWhenTrue.isDefinitelyNull(local) 
119
				&& initsWhenFalse.isDefinitelyNull(local);
120
	}
121
95
122
	public int reachMode(){
96
public boolean isDefinitelyUnknown(LocalVariableBinding local) {
123
		return unconditionalInits().reachMode();
97
	return initsWhenTrue.isDefinitelyUnknown(local) 
124
	}
98
			&& initsWhenFalse.isDefinitelyUnknown(local);
125
	
99
}
126
	public boolean isReachable(){
127
		
128
		return unconditionalInits().isReachable();	
129
		//should maybe directly be: false
130
	}
131
	
132
	/**
100
	/**
133
	 * Check status of potential assignment for a field.
101
	 * Check status of potential assignment for a field.
134
	 */
102
	 */
Lines 147-152 Link Here
147
				|| initsWhenFalse.isPotentiallyAssigned(local);
115
				|| initsWhenFalse.isPotentiallyAssigned(local);
148
	}
116
	}
149
	
117
	
118
public boolean isPotentiallyNull(LocalVariableBinding local) {
119
	return initsWhenTrue.isPotentiallyNull(local) 
120
		|| initsWhenFalse.isPotentiallyNull(local);
121
}	
122
123
public boolean isPotentiallyUnknown(LocalVariableBinding local) {
124
	return initsWhenTrue.isPotentiallyUnknown(local) 
125
		|| initsWhenFalse.isPotentiallyUnknown(local);
126
}	
127
128
public boolean isProtectedNonNull(LocalVariableBinding local) {
129
	return initsWhenTrue.isProtectedNonNull(local) 
130
		&& initsWhenFalse.isProtectedNonNull(local);
131
}		
132
	
133
public boolean isProtectedNull(LocalVariableBinding local) {
134
	return initsWhenTrue.isProtectedNull(local) 
135
		&& initsWhenFalse.isProtectedNull(local);
136
}		
137
	
138
public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
139
	initsWhenTrue.markAsComparedEqualToNonNull(local);
140
	initsWhenFalse.markAsComparedEqualToNonNull(local);
141
}
142
143
public void markAsComparedEqualToNull(LocalVariableBinding local) {
144
	initsWhenTrue.markAsComparedEqualToNull(local);
145
    initsWhenFalse.markAsComparedEqualToNull(local);
146
}
147
	
150
	/**
148
	/**
151
	 * Record a field got definitely assigned.
149
	 * Record a field got definitely assigned.
152
	 */
150
	 */
Lines 201-250 Link Here
201
		initsWhenFalse.markAsDefinitelyNull(local);	
199
		initsWhenFalse.markAsDefinitelyNull(local);	
202
	}
200
	}
203
201
204
	/**
202
public void markAsDefinitelyUnknown(LocalVariableBinding local) {
205
	 * Clear the initialization info for a field
203
	initsWhenTrue.markAsDefinitelyUnknown(local);
206
	 */
204
	initsWhenFalse.markAsDefinitelyUnknown(local);	
207
	public void markAsDefinitelyNotAssigned(FieldBinding field) {
205
}
208
		
206
209
		initsWhenTrue.markAsDefinitelyNotAssigned(field);
207
public FlowInfo setReachMode(int reachMode) {
210
		initsWhenFalse.markAsDefinitelyNotAssigned(field);	
208
	if (reachMode == REACHABLE) {
211
	}
209
		this.tagBits &= ~UNREACHABLE;
212
	
210
	}
213
	/**
211
	else {
214
	 * Clear the initialization info for a local variable
212
		this.tagBits |= UNREACHABLE;
215
	 */
213
	}
216
	public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
214
	initsWhenTrue.setReachMode(reachMode);
217
		
215
	initsWhenFalse.setReachMode(reachMode);
218
		initsWhenTrue.markAsDefinitelyNotAssigned(local);
216
	return this;
219
		initsWhenFalse.markAsDefinitelyNotAssigned(local);	
217
}
220
	}
221
	
222
	public FlowInfo setReachMode(int reachMode) {
223
		
224
		initsWhenTrue.setReachMode(reachMode);
225
		initsWhenFalse.setReachMode(reachMode);
226
		return this;
227
	}
228
	
229
	/**
230
	 * Converts conditional receiver into inconditional one, updated in the following way: <ul>
231
	 * <li> intersection of definitely assigned variables, 
232
	 * <li> union of potentially assigned variables.
233
	 * </ul>
234
	 */
235
	public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
236
		
237
		return unconditionalInits().mergedWith(otherInits);
238
	}
239
	
218
	
219
public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
220
	return unconditionalInits().mergedWith(otherInits);
221
}
222
223
public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() {
224
	return unconditionalInitsWithoutSideEffect().
225
		nullInfoLessUnconditionalCopy();
226
}
240
	public String toString() {
227
	public String toString() {
241
		
228
		
242
		return "FlowInfo<true: " + initsWhenTrue.toString() + ", false: " + initsWhenFalse.toString() + ">"; //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
229
		return "FlowInfo<true: " + initsWhenTrue.toString() + ", false: " + initsWhenFalse.toString() + ">"; //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-2$
243
	}
230
	}
244
	
231
245
	public UnconditionalFlowInfo unconditionalInits() {
232
public FlowInfo safeInitsWhenTrue() {
246
		
233
	return initsWhenTrue;
247
		return initsWhenTrue.unconditionalInits().copy()
234
}
248
				.mergedWith(initsWhenFalse.unconditionalInits());
235
249
	}
236
public UnconditionalFlowInfo unconditionalCopy() {
237
	return initsWhenTrue.unconditionalCopy().
238
			mergedWith(initsWhenFalse.unconditionalInits());
239
}
240
241
public UnconditionalFlowInfo unconditionalFieldLessCopy() {
242
	return initsWhenTrue.unconditionalFieldLessCopy().
243
		mergedWith(initsWhenFalse.unconditionalFieldLessCopy()); 
244
	// should never happen, hence suboptimal does not hurt
245
}
246
247
public UnconditionalFlowInfo unconditionalInits() {
248
	return initsWhenTrue.unconditionalInits().
249
			mergedWith(initsWhenFalse.unconditionalInits());
250
}
251
252
public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() {
253
	// cannot do better here than unconditionalCopy - but still a different 
254
	// operation for UnconditionalFlowInfo
255
	return initsWhenTrue.unconditionalCopy().
256
			mergedWith(initsWhenFalse.unconditionalInits());
257
}
250
}
258
}
(-)compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionHandlingFlowContext.java (-12 / +14 lines)
Lines 62-68 Link Here
62
			int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize);
62
			int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize);
63
			if (handledExceptions[i].isUncheckedException(true)) {
63
			if (handledExceptions[i].isUncheckedException(true)) {
64
				isReached[cacheIndex] |= bitMask;
64
				isReached[cacheIndex] |= bitMask;
65
				this.initsOnExceptions[i] = flowInfo.copy().unconditionalInits();
65
				this.initsOnExceptions[i] = flowInfo.unconditionalCopy();
66
			} else {
66
			} else {
67
				this.initsOnExceptions[i] = FlowInfo.DEAD_END;
67
				this.initsOnExceptions[i] = FlowInfo.DEAD_END;
68
			}
68
			}
Lines 168-187 Link Here
168
		this.isReached[cacheIndex] |= bitMask;
168
		this.isReached[cacheIndex] |= bitMask;
169
		
169
		
170
		initsOnExceptions[index] =
170
		initsOnExceptions[index] =
171
			initsOnExceptions[index] == FlowInfo.DEAD_END
171
			initsOnExceptions[index].isReachable() ?
172
				? flowInfo.copy().unconditionalInits()
172
				initsOnExceptions[index].mergedWith(flowInfo):
173
				: initsOnExceptions[index].mergedWith(flowInfo.copy().unconditionalInits());
173
				flowInfo.unconditionalCopy();
174
	}
174
	}
175
	
175
	
176
	public void recordReturnFrom(FlowInfo flowInfo) {
176
public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
177
177
	if (!flowInfo.isReachable()) {
178
		if (!flowInfo.isReachable()) return; 
178
		return; 
179
		if (initsOnReturn == FlowInfo.DEAD_END) {
179
	}
180
			initsOnReturn = flowInfo.copy().unconditionalInits();
180
	if (initsOnReturn.isReachable()) {
181
		} else {
181
		initsOnReturn = initsOnReturn.mergedWith(flowInfo);
182
			initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits());
182
	} 
183
		}
183
	else {
184
		initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy();
184
	}
185
	}
186
}
185
	
187
	
186
	/*
188
	/*
187
	 * Compute a merged list of unhandled exception types (keeping only the most generic ones).
189
	 * Compute a merged list of unhandled exception types (keeping only the most generic ones).
(-)compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java (-65 / +189 lines)
Lines 16-21 Link Here
16
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
16
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
17
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
17
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
18
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
18
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
19
import org.eclipse.jdt.internal.compiler.lookup.Scope;
19
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
20
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
20
21
21
/**
22
/**
Lines 28-104 Link Here
28
	VariableBinding[] finalVariables;
29
	VariableBinding[] finalVariables;
29
	int assignCount;
30
	int assignCount;
30
31
32
	LocalVariableBinding[] nullLocals;	
31
	Expression[] nullReferences;
33
	Expression[] nullReferences;
32
	int[] nullStatus;
34
	int[] nullCheckTypes;
33
	int nullCount;
35
	int nullCount;
34
	
36
	
35
	public FinallyFlowContext(FlowContext parent, ASTNode associatedNode) {
37
	public FinallyFlowContext(FlowContext parent, ASTNode associatedNode) {
36
		super(parent, associatedNode);
38
		super(parent, associatedNode);
37
	}
39
	}
38
40
39
	/**
41
/**
40
	 * Given some contextual initialization info (derived from a try block or a catch block), this 
42
 * Given some contextual initialization info (derived from a try block or a catch block), this 
41
	 * code will check that the subroutine context does not also initialize a final variable potentially set
43
 * code will check that the subroutine context does not also initialize a final variable potentially set
42
	 * redundantly.
44
 * redundantly.
43
	 */
45
 */
44
	public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) {
46
public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) {
47
	
48
	// check redundant final assignments
49
	for (int i = 0; i < this.assignCount; i++) {
50
		VariableBinding variable = this.finalVariables[i];
51
		if (variable == null) continue;
45
		
52
		
46
		// check redundant final assignments
53
		boolean complained = false; // remember if have complained on this final assignment
47
		for (int i = 0; i < assignCount; i++) {
54
		if (variable instanceof FieldBinding) {
48
			VariableBinding variable = finalVariables[i];
55
			// final field
49
			if (variable == null) continue;
56
			if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) {
50
			
57
				complained = true;
51
			boolean complained = false; // remember if have complained on this final assignment
58
				scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, finalAssignments[i]);
52
			if (variable instanceof FieldBinding) {
59
			}
53
				// final field
60
		} else {
54
				if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) {
61
			// final local variable
55
					complained = true;
62
			if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
56
					scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, finalAssignments[i]);
63
				complained = true;
57
				}
64
				scope.problemReporter().duplicateInitializationOfFinalLocal(
58
			} else {
65
					(LocalVariableBinding) variable,
59
				// final local variable
66
					this.finalAssignments[i]);
60
				if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
61
					complained = true;
62
					scope.problemReporter().duplicateInitializationOfFinalLocal(
63
						(LocalVariableBinding) variable,
64
						finalAssignments[i]);
65
				}
66
			}
67
			// any reference reported at this level is removed from the parent context 
68
			// where it could also be reported again
69
			if (complained) {
70
				FlowContext currentContext = parent;
71
				while (currentContext != null) {
72
					//if (currentContext.isSubRoutine()) {
73
					currentContext.removeFinalAssignmentIfAny(finalAssignments[i]);
74
					//}
75
					currentContext = currentContext.parent;
76
				}
77
			}
67
			}
78
		}
68
		}
79
		
69
		// any reference reported at this level is removed from the parent context 
80
		// check inconsistent null checks
70
		// where it could also be reported again
81
		for (int i = 0; i < nullCount; i++) {
71
		if (complained) {
82
			Expression expression = nullReferences[i];
72
			FlowContext currentContext = this.parent;
83
			if (expression == null) continue;
73
			while (currentContext != null) {
74
				//if (currentContext.isSubRoutine()) {
75
				currentContext.removeFinalAssignmentIfAny(this.finalAssignments[i]);
76
				//}
77
				currentContext = currentContext.parent;
78
			}
79
		}
80
	}
81
	
82
	// check inconsistent null checks
83
	if (this.deferNullDiagnostic) { // within an enclosing loop, be conservative
84
		for (int i = 0; i < this.nullCount; i++) {
85
			Expression expression = this.nullReferences[i];
86
			LocalVariableBinding local = this.nullLocals[i];
87
			switch (this.nullCheckTypes[i]) {
88
				case CAN_ONLY_NULL_NON_NULL :
89
				case CAN_ONLY_NULL:
90
					if (flowInfo.isProtectedNonNull(local)) {
91
						if (nullCheckTypes[i] == CAN_ONLY_NULL_NON_NULL) {
92
							scope.problemReporter().localVariableCannotBeNull(local, expression);
93
						}
94
						return;
95
					}
96
					if (flowInfo.isProtectedNull(local)) {
97
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
98
						return;
99
					}
100
					break;
101
				case MAY_NULL :
102
					if (flowInfo.isProtectedNonNull(local)) {
103
						return;
104
					}
105
					if (flowInfo.isProtectedNull(local)) {
106
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
107
						return;
108
					}
109
					break;
110
				default:
111
					// never happens
112
			}
113
			this.parent.recordUsingNullReference(scope, local, expression, 
114
					this.nullCheckTypes[i], flowInfo);
115
		}
116
	}
117
	else { // no enclosing loop, be as precise as possible right now
118
		for (int i = 0; i < this.nullCount; i++) {
119
			Expression expression = this.nullReferences[i];
84
			// final local variable
120
			// final local variable
85
			LocalVariableBinding local = expression.localVariableBinding();
121
			LocalVariableBinding local = this.nullLocals[i];
86
			switch (nullStatus[i]) {
122
			switch (this.nullCheckTypes[i]) {
87
				case FlowInfo.NULL :
123
				case CAN_ONLY_NULL_NON_NULL :
124
					if (flowInfo.isDefinitelyNonNull(local)) {
125
						scope.problemReporter().localVariableCannotBeNull(local, expression);				
126
						return;
127
					}
128
				case CAN_ONLY_NULL:
88
					if (flowInfo.isDefinitelyNull(local)) {
129
					if (flowInfo.isDefinitelyNull(local)) {
89
						nullReferences[i] = null;
130
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
90
						this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo);
131
						return;
91
					}
132
					}
92
					break;
133
					break;
93
				case FlowInfo.NON_NULL :
134
				case MAY_NULL :
94
					if (flowInfo.isDefinitelyNonNull(local)) {
135
					if (flowInfo.isDefinitelyNull(local)) {
95
						nullReferences[i] = null;
136
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
96
						this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo);
137
						return;
138
					}
139
					if (flowInfo.isPotentiallyNull(local)) {
140
						scope.problemReporter().localVariableMayBeNull(local, expression);
141
						return;
97
					}
142
					}
98
					break;
143
					break;
144
				default:
145
					// should not happen
99
			}
146
			}
100
		}
147
		}
101
	}
148
	}
149
}
102
	
150
	
103
	public String individualToString() {
151
	public String individualToString() {
104
		
152
		
Lines 138-143 Link Here
138
		return true;
186
		return true;
139
	}
187
	}
140
188
189
	public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 
190
			Expression reference, int checkType, FlowInfo flowInfo) {
191
		if (!flowInfo.isReachable()) {
192
			return;
193
		}
194
		if (deferNullDiagnostic) { // within an enclosing loop, be conservative
195
			switch (checkType) {
196
				case CAN_ONLY_NULL_NON_NULL :
197
				case CAN_ONLY_NULL:
198
					if (flowInfo.isProtectedNonNull(local)) {
199
						if (checkType == CAN_ONLY_NULL_NON_NULL) {
200
							scope.problemReporter().localVariableCannotBeNull(local, reference);
201
						}
202
						return;
203
					}
204
					if (flowInfo.isProtectedNull(local)) {
205
						scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
206
						return;
207
					}
208
					break;
209
				case MAY_NULL :
210
					if (flowInfo.isProtectedNonNull(local)) {
211
						return;
212
					}
213
					if (flowInfo.isProtectedNull(local)) {
214
						scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
215
						return;
216
					}
217
					break;
218
				default:
219
					// never happens
220
			}
221
		}
222
		else { // no enclosing loop, be as precise as possible right now
223
			switch (checkType) {
224
				case CAN_ONLY_NULL_NON_NULL :
225
					if (flowInfo.isDefinitelyNonNull(local)) {
226
						scope.problemReporter().localVariableCannotBeNull(local, reference);				
227
						return;
228
					}
229
				case CAN_ONLY_NULL:
230
					if (flowInfo.isDefinitelyNull(local)) {
231
						scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
232
						return;
233
					}
234
					break;
235
				case MAY_NULL :
236
					if (flowInfo.isDefinitelyNull(local)) {
237
						scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
238
						return;
239
					}
240
					if (flowInfo.isPotentiallyNull(local)) {
241
						scope.problemReporter().localVariableMayBeNull(local, reference);
242
						return;
243
					}
244
					if (flowInfo.isDefinitelyNonNull(local)) {
245
						return; // shortcut: cannot be null
246
					}
247
					break;
248
				default:
249
					// never happens
250
			}
251
		}
252
		recordNullReference(local, reference, checkType); 
253
		// prepare to re-check with try/catch flow info 
254
	}
255
	
141
	void removeFinalAssignmentIfAny(Reference reference) {
256
	void removeFinalAssignmentIfAny(Reference reference) {
142
		for (int i = 0; i < assignCount; i++) {
257
		for (int i = 0; i < assignCount; i++) {
143
			if (finalAssignments[i] == reference) {
258
			if (finalAssignments[i] == reference) {
Lines 148-165 Link Here
148
		}
263
		}
149
	}
264
	}
150
265
151
	protected boolean recordNullReference(Expression expression, int status) {
266
protected void recordNullReference(LocalVariableBinding local, 
152
		if (nullCount == 0) {
267
	Expression expression, int status) {
153
			nullReferences = new Expression[5];
268
	if (this.nullCount == 0) {
154
			nullStatus = new int[5];
269
		this.nullLocals = new LocalVariableBinding[5];
155
		} else {
270
		this.nullReferences = new Expression[5];
156
			if (nullCount == nullReferences.length) {
271
		this.nullCheckTypes = new int[5];
157
				System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount);
272
	} 
158
				System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount);
273
	else if (this.nullCount == this.nullLocals.length) {
159
			}
274
		int newLength = this.nullCount * 2;
160
		}
275
		System.arraycopy(this.nullLocals, 0, 
161
		nullReferences[nullCount] = expression;
276
			this.nullLocals = new LocalVariableBinding[newLength], 0, 
162
		nullStatus[nullCount++] = status;
277
			this.nullCount);
163
		return true;
278
		System.arraycopy(this.nullReferences, 0, 
279
			this.nullReferences = new Expression[newLength], 0,
280
			this.nullCount);
281
		System.arraycopy(this.nullCheckTypes, 0, 
282
			this.nullCheckTypes = new int[newLength], 0, 
283
			this.nullCount);
164
	}
284
	}
285
	this.nullLocals[this.nullCount] = local;
286
	this.nullReferences[this.nullCount] = expression;
287
	this.nullCheckTypes[this.nullCount++] = status;
288
}
165
}
289
}
(-)compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java (-24 / +75 lines)
Lines 35-46 Link Here
35
	
35
	
36
	public ASTNode associatedNode;
36
	public ASTNode associatedNode;
37
	public FlowContext parent;
37
	public FlowContext parent;
38
38
	boolean deferNullDiagnostic, preemptNullDiagnostic; 
39
		// preempt marks looping contexts
39
	public final static FlowContext NotContinuableContext = new FlowContext(null, null);
40
	public final static FlowContext NotContinuableContext = new FlowContext(null, null);
40
		
41
		
41
public FlowContext(FlowContext parent, ASTNode associatedNode) {
42
public FlowContext(FlowContext parent, ASTNode associatedNode) {
42
	this.parent = parent;
43
	this.parent = parent;
43
	this.associatedNode = associatedNode;
44
	this.associatedNode = associatedNode;
45
	deferNullDiagnostic = parent != null && 
46
		(parent.deferNullDiagnostic || parent.preemptNullDiagnostic); 
44
}
47
}
45
48
46
public Label breakLabel() {
49
public Label breakLabel() {
Lines 163-169 Link Here
163
		traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
166
		traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
164
		if (traversedContext.associatedNode instanceof TryStatement){
167
		if (traversedContext.associatedNode instanceof TryStatement){
165
			TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
168
			TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
166
			flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
169
				flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits);
167
		}
170
		}
168
		traversedContext = traversedContext.parent;
171
		traversedContext = traversedContext.parent;
169
	}
172
	}
Lines 256-262 Link Here
256
		traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
259
		traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
257
		if (traversedContext.associatedNode instanceof TryStatement){
260
		if (traversedContext.associatedNode instanceof TryStatement){
258
			TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
261
			TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
259
			flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
262
				flowInfo = flowInfo.addInitializationsFrom(tryStatement.subRoutineInits);
260
		}
263
		}
261
		traversedContext = traversedContext.parent;
264
		traversedContext = traversedContext.parent;
262
	}
265
	}
Lines 403-409 Link Here
403
	// default implementation: do nothing
406
	// default implementation: do nothing
404
}
407
}
405
408
406
public void recordContinueFrom(FlowInfo flowInfo) {
409
public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) {
407
	// default implementation: do nothing
410
	// default implementation: do nothing
408
}
411
}
409
412
Lines 411-421 Link Here
411
	return true; // keep going
414
	return true; // keep going
412
}
415
}
413
416
414
protected boolean recordNullReference(Expression expression, int status) {
417
/**
415
	return false; // keep going
418
 * Record a null reference for use by deferred checks. Only looping or 
419
 * finally contexts really record that information.
420
 * @param local the local variable involved in the check
421
 * @param expression the expression within which local lays
422
 * @param status the status against which the check must be performed; one of
423
 * 		{@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL 
424
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL} 
425
 */
426
protected void recordNullReference(LocalVariableBinding local, 
427
	Expression expression, int status) {
428
	// default implementation: do nothing
416
}
429
}
417
430
418
public void recordReturnFrom(FlowInfo flowInfo) {
431
public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
419
	// default implementation: do nothing
432
	// default implementation: do nothing
420
}
433
}
421
434
Lines 432-465 Link Here
432
	}
445
	}
433
}
446
}
434
447
435
public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int status, FlowInfo flowInfo) {
448
public static final int 
436
	if (!flowInfo.isReachable()) return;
449
  CAN_ONLY_NULL_NON_NULL = 20, 
437
450
  	// check against null and non null, with definite values -- comparisons
438
	switch (status) {
451
  CAN_ONLY_NULL = 21,
439
		case FlowInfo.NULL :
452
  	// check against null, with definite values -- assignment to null
453
  MAY_NULL = 22;
454
		// check against null, with potential values -- NPE guard
455
456
/**
457
 * Record a null reference for use by deferred checks. Only looping or 
458
 * finally contexts really record that information. The context may
459
 * emit an error immediately depending on the status of local against
460
 * flowInfo and its nature (only looping of finally contexts defer part
461
 * of the checks; nonetheless, contexts that are nested into a looping or a
462
 * finally context get affected and delegate some checks to their enclosing
463
 * context).
464
 * @param scope the scope into which the check is performed
465
 * @param local the local variable involved in the check
466
 * @param reference the expression within which local lays
467
 * @param checkType the status against which the check must be performed; one 
468
 * 		of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL 
469
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}
470
 * @param flowInfo the flow info at the check point; deferring contexts will
471
 *  	perform supplementary checks against flow info instances that cannot
472
 *  	be known at the time of calling this method (they are influenced by
473
 * 		code that follows the current point)
474
 */
475
public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 
476
		Expression reference, int checkType, FlowInfo flowInfo) {
477
	if (!flowInfo.isReachable() || flowInfo.isDefinitelyUnknown(local)) {
478
		return;
479
	}
480
	switch (checkType) {
481
		case CAN_ONLY_NULL_NON_NULL :
482
			if (flowInfo.isDefinitelyNonNull(local)) {
483
				scope.problemReporter().localVariableCannotBeNull(local, reference);				
484
				return;
485
			}
486
			else if (flowInfo.isPotentiallyUnknown(local)) {
487
				return;
488
			}
489
		case CAN_ONLY_NULL:
440
			if (flowInfo.isDefinitelyNull(local)) {
490
			if (flowInfo.isDefinitelyNull(local)) {
441
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
491
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
442
				return;
492
				return;
443
			} else if (flowInfo.isDefinitelyNonNull(local)) {
493
			}
444
				scope.problemReporter().localVariableCannotBeNull(local, reference);				
494
			else if (flowInfo.isPotentiallyUnknown(local)) {
445
				return;
495
				return;
446
			}
496
			}
447
			break;
497
			break;
448
		case FlowInfo.NON_NULL :
498
		case MAY_NULL :
449
			if (flowInfo.isDefinitelyNull(local)) {
499
			if (flowInfo.isDefinitelyNull(local)) {
450
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);				
500
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
501
				return;
502
			}
503
			if (flowInfo.isPotentiallyNull(local)) {
504
				scope.problemReporter().localVariableMayBeNull(local, reference);
451
				return;
505
				return;
452
			}
506
			}
453
			break;
507
			break;
508
		default:
509
			// never happens
454
	}
510
	}
455
	
511
	if (parent != null) {
456
	// for initialization inside looping statement that effectively loops
512
		parent.recordUsingNullReference(scope, local, reference, checkType, 
457
	FlowContext context = this;
513
				flowInfo);
458
	while (context != null) {
459
		if (context.recordNullReference(reference, status)) {
460
			return; // no need to keep going
461
		}
462
		context = context.parent;
463
	}
514
	}
464
}
515
}
465
516
(-)compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java (-69 / +234 lines)
Lines 15-22 Link Here
15
15
16
public abstract class FlowInfo {
16
public abstract class FlowInfo {
17
17
18
	public int tagBits; // REACHABLE by default
18
	public final static int REACHABLE = 0;
19
	public final static int REACHABLE = 0;
19
	public final static int UNREACHABLE = 1;
20
	public final static int UNREACHABLE = 1;
21
	public final static int NULL_FLAG_MASK = 2;
20
	
22
	
21
	public final static int UNKNOWN = 0;
23
	public final static int UNKNOWN = 0;
22
	public final static int NULL = 1;
24
	public final static int NULL = 1;
Lines 25-35 Link Here
25
	public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization
27
	public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization
26
	static {
28
	static {
27
		DEAD_END = new UnconditionalFlowInfo();
29
		DEAD_END = new UnconditionalFlowInfo();
28
		DEAD_END.reachMode = UNREACHABLE;
30
		DEAD_END.tagBits = UNREACHABLE;
29
	}
31
	}
30
	abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits);
31
32
32
	abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits);
33
/**
34
 * Add other inits to this flow info, then return this. The operation semantics
35
 * are to match as closely as possible the application to this flow info of all
36
 * the operations that resulted into otherInits. 
37
 * @param otherInits other inits to add to this
38
 * @return this, modified according to otherInits information
39
 */
40
abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits);
41
42
43
/**
44
 * Compose other inits over this flow info, then return this. The operation
45
 * semantics are to wave into this flow info the consequences of a possible
46
 * path into the operations that resulted into otherInits. The fact that this
47
 * path may be left unexecuted under peculiar conditions results into less
48
 * specific results than {@link #addInitializationsFrom(FlowInfo) 
49
 * addInitializationsFrom}.
50
 * @param otherInits other inits to compose over this
51
 * @return this, modified according to otherInits information
52
 */
53
abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits);
33
54
34
	public FlowInfo asNegatedCondition() {
55
	public FlowInfo asNegatedCondition() {
35
56
Lines 42-47 Link Here
42
		return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse);
63
		return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse);
43
	}
64
	}
44
65
66
/**
67
 * Return a deep copy of the current instance.
68
 * @return a deep copy of this flow info
69
 */
45
	abstract public FlowInfo copy();
70
	abstract public FlowInfo copy();
46
71
47
	public static UnconditionalFlowInfo initial(int maxFieldCount) {
72
	public static UnconditionalFlowInfo initial(int maxFieldCount) {
Lines 49-57 Link Here
49
		info.maxFieldCount = maxFieldCount;
74
		info.maxFieldCount = maxFieldCount;
50
		return info;
75
		return info;
51
	}
76
	}
52
77
	
53
	abstract public FlowInfo initsWhenFalse();
78
/**
54
79
 * Return the flow info that would result from the path associated to the
80
 * value false for the condition expression that generated this flow info.
81
 * May be this flow info if it is not an instance of {@link 
82
 * ConditionalFlowInfo}. May have a side effect on subparts of this flow
83
 * info (subtrees get merged).
84
 * @return the flow info associated to the false branch of the condition
85
 * 			that generated this flow info
86
 */
87
abstract public FlowInfo initsWhenFalse();
88
89
/**
90
 * Return the flow info that would result from the path associated to the
91
 * value true for the condition expression that generated this flow info.
92
 * May be this flow info if it is not an instance of {@link 
93
 * ConditionalFlowInfo}. May have a side effect on subparts of this flow
94
 * info (subtrees get merged).
95
 * @return the flow info associated to the true branch of the condition
96
 * 			that generated this flow info
97
 */
55
	abstract public FlowInfo initsWhenTrue();
98
	abstract public FlowInfo initsWhenTrue();
56
99
57
	/**
100
	/**
Lines 64-88 Link Here
64
	 */
107
	 */
65
	public abstract boolean isDefinitelyAssigned(LocalVariableBinding local);
108
	public abstract boolean isDefinitelyAssigned(LocalVariableBinding local);
66
109
67
	/**
110
/**
68
	 * Check status of definite null assignment for a field.
111
 * Check status of definite non-null value for a given local variable.
69
	 */
112
 * @param local the variable to ckeck
70
	 abstract public boolean isDefinitelyNonNull(FieldBinding field);   
113
 * @return true iff local is definitely non null for this flow info
71
114
 */
72
	/**
73
	 * Check status of definite null assignment for a local.
74
	 */
75
	public abstract boolean isDefinitelyNonNull(LocalVariableBinding local);
115
	public abstract boolean isDefinitelyNonNull(LocalVariableBinding local);
76
116
77
	/**
117
/**
78
	 * Check status of definite null assignment for a field.
118
 * Check status of definite null value for a given local variable.
79
	 */
119
 * @param local the variable to ckeck
80
	 abstract public boolean isDefinitelyNull(FieldBinding field);   
120
 * @return true iff local is definitely null for this flow info
81
121
 */
82
	/**
122
public abstract boolean isDefinitelyNull(LocalVariableBinding local);
83
	 * Check status of definite null assignment for a local.
123
84
	 */
124
/**
85
	public abstract boolean isDefinitelyNull(LocalVariableBinding local);
125
 * Check status of definite unknown value for a given local variable.
126
 * @param local the variable to ckeck
127
 * @return true iff local is definitely unknown for this flow info
128
 */
129
public abstract boolean isDefinitelyUnknown(LocalVariableBinding local);
86
130
87
	/**
131
	/**
88
	 * Check status of potential assignment for a field.
132
	 * Check status of potential assignment for a field.
Lines 95-101 Link Here
95
139
96
	 abstract public boolean isPotentiallyAssigned(LocalVariableBinding field);   
140
	 abstract public boolean isPotentiallyAssigned(LocalVariableBinding field);   
97
141
98
	abstract public boolean isReachable();
142
/**
143
 * Check status of potential null assignment for a local.
144
 */
145
public abstract boolean isPotentiallyNull(LocalVariableBinding local);
146
	
147
/**
148
 * Return true if the given local may have been assigned to an unknown value.
149
 * @param local the local to check
150
 * @return true if the given local may have been assigned to an unknown value
151
 */
152
public abstract boolean isPotentiallyUnknown(LocalVariableBinding local);
153
154
/**
155
 * Return true if the given local is protected by a test against a non null
156
 * value.
157
 * @param local the local to check
158
 * @return true if the given local is protected by a test against a non null
159
 */
160
public abstract boolean isProtectedNonNull(LocalVariableBinding local);
161
162
/**
163
 * Return true if the given local is protected by a test against null.
164
 * @param local the local to check
165
 * @return true if the given local is protected by a test against null
166
 */
167
public abstract boolean isProtectedNull(LocalVariableBinding local);
168
169
public boolean isReachable() {
170
	return (this.tagBits & UNREACHABLE) == 0;
171
}
172
173
/**
174
 * Record that a local variable got checked to be non null.
175
 * @param local the checked local variable
176
 */
177
abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local);
178
	
179
/**
180
 * Record that a local variable got checked to be null.
181
 * @param local the checked local variable
182
 */
183
abstract public void markAsComparedEqualToNull(LocalVariableBinding local);
99
	
184
	
100
	/**
185
	/**
101
	 * Record a field got definitely assigned.
186
	 * Record a field got definitely assigned.
Lines 118-124 Link Here
118
	abstract public void markAsDefinitelyNull(LocalVariableBinding local);
203
	abstract public void markAsDefinitelyNull(LocalVariableBinding local);
119
204
120
	/**
205
	/**
121
	 * Record a field got definitely assigned.
206
	 * Record a field got definitely assigned to null.
122
	 */
207
	 */
123
	abstract public void markAsDefinitelyNull(FieldBinding field);
208
	abstract public void markAsDefinitelyNull(FieldBinding field);
124
209
Lines 127-178 Link Here
127
	 */
212
	 */
128
	abstract public void markAsDefinitelyAssigned(LocalVariableBinding local);
213
	abstract public void markAsDefinitelyAssigned(LocalVariableBinding local);
129
214
130
	/**
215
/**
131
	 * Clear the initialization info for a field
216
 * Record a local got definitely assigned to an unknown value.
132
	 */
217
 */
133
	abstract public void markAsDefinitelyNotAssigned(FieldBinding field);
218
abstract public void markAsDefinitelyUnknown(LocalVariableBinding local);
134
219
135
	/**
220
/**
136
	 * Clear the initialization info for a local variable
221
 * Merge branches using optimized boolean conditions
137
	 */
222
 */
138
	abstract public void markAsDefinitelyNotAssigned(LocalVariableBinding local);
223
public static UnconditionalFlowInfo mergedOptimizedBranches(
139
224
		FlowInfo initsWhenTrue, boolean isOptimizedTrue, 
140
	/**
225
		FlowInfo initsWhenFalse, boolean isOptimizedFalse, 
141
	 * Merge branches using optimized boolean conditions
226
		boolean allowFakeDeadBranch) {
142
	 */
227
	UnconditionalFlowInfo mergedInfo;
143
	public static FlowInfo mergedOptimizedBranches(FlowInfo initsWhenTrue, boolean isOptimizedTrue, FlowInfo initsWhenFalse, boolean isOptimizedFalse, boolean allowFakeDeadBranch) {
228
	if (isOptimizedTrue){
144
		FlowInfo mergedInfo;
229
		if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) {
145
		if (isOptimizedTrue){
230
			mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE).
146
			if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) {
231
				unconditionalInits();
147
				mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
232
		} 
148
			} else {
233
		else {
149
				mergedInfo = initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse);
234
			mergedInfo = 
150
			}
235
				initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse.
151
236
					nullInfoLessUnconditionalCopy()).
152
		} else if (isOptimizedFalse) {
237
				unconditionalInits();
153
			if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
238
		}
154
				mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
239
	} 
155
			} else {
240
	else if (isOptimizedFalse) {
156
				mergedInfo = initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue);
241
		if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
157
			}
242
			mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE).
158
243
				unconditionalInits();
159
		} else {
244
		} 
160
			mergedInfo = initsWhenTrue.unconditionalInits().mergedWith(initsWhenFalse.unconditionalInits());
245
		else {
246
			mergedInfo = 
247
				initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue.
248
					nullInfoLessUnconditionalCopy()).
249
				unconditionalInits();
161
		}
250
		}
162
		return mergedInfo;
251
	} 
252
	else {
253
		mergedInfo = initsWhenTrue.
254
			mergedWith(initsWhenFalse.unconditionalInits());
163
	}
255
	}
256
	return mergedInfo;
257
}
164
	
258
	
165
	abstract public int reachMode();
259
/**
166
260
 * Return REACHABLE if this flow info is reachable, UNREACHABLE
167
	abstract public FlowInfo setReachMode(int reachMode);
261
 * else.
168
262
 * @return REACHABLE if this flow info is reachable, UNREACHABLE
169
	/**
263
 *         else
170
	 * Returns the receiver updated in the following way: <ul>
264
 */
171
	 * <li> intersection of definitely assigned variables, 
265
public int reachMode() {
172
	 * <li> union of potentially assigned variables.
266
	return this.tagBits & UNREACHABLE;
173
	 * </ul>
267
}
174
	 */
268
	
175
	abstract public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits);
269
/**
270
 * Return a flow info that carries the same information as the result of
271
 * {@link #initsWhenTrue() initsWhenTrue}, but warrantied to be different 
272
 * from this.<br>
273
 * Caveat: side effects on the result may affect components of this. 
274
 * @return the result of initsWhenTrue or a copy of it
275
 */
276
abstract public FlowInfo safeInitsWhenTrue();
277
278
/**
279
 * Set this flow info reach mode and return this.
280
 * @param reachMode one of {@link #REACHABLE REACHABLE} or {@link #UNREACHABLE UNREACHABLE}
281
 * @return this, with the reach mode set to reachMode
282
 */
283
abstract public FlowInfo setReachMode(int reachMode);
284
285
/**
286
 * Return the intersection of this and otherInits, that is 
287
 * one of:<ul>
288
 *   <li>the receiver updated in the following way:<ul>
289
 *     <li>intersection of definitely assigned variables, 
290
 *     <li>union of potentially assigned variables,
291
 *     <li>similar operations for null,</ul>
292
 *   <li>or the receiver or otherInits if the other one is non
293
 *       reachable.</ul>
294
 * otherInits is not affected, and is not returned either (no
295
 * need to protect the result).
296
 * @param otherInits the flow info to merge with this
297
 * @return the intersection of this and otherInits.
298
 */
299
abstract public UnconditionalFlowInfo mergedWith(
300
		UnconditionalFlowInfo otherInits);
301
302
/**
303
 * Return a copy of this unconditional flow info, deprived from its null
304
 * info. {@link #DEAD_END DEAD_END} is returned unmodified.
305
 * @return a copy of this unconditional flow info deprived from its null info
306
 */
307
abstract public UnconditionalFlowInfo nullInfoLessUnconditionalCopy();
176
308
177
	public String toString(){
309
	public String toString(){
178
310
Lines 182-186 Link Here
182
		return super.toString();
314
		return super.toString();
183
	}
315
	}
184
316
185
	abstract public UnconditionalFlowInfo unconditionalInits();
317
/**
318
 * Return a new flow info that holds the same information as this would after
319
 * a call to unconditionalInits, but leaving this info unaffected. Moreover,
320
 * the result can be modified without affecting this.
321
 * @return a new flow info carrying this unconditional flow info
322
 */
323
abstract public UnconditionalFlowInfo unconditionalCopy();
324
325
/**
326
 * Return a new flow info that holds the same information as this would after
327
 * a call to {@link #unconditionalInits() unconditionalInits} followed by the 
328
 * erasure of fields specific information, but leaving this flow info unaffected.
329
 * @return a new flow info carrying the unconditional flow info for local variables
330
 */
331
abstract public UnconditionalFlowInfo unconditionalFieldLessCopy();
332
333
/**
334
 * Return a flow info that merges the possible paths of execution described by
335
 * this flow info. In case of an unconditional flow info, return this. In case
336
 * of a conditional flow info, merge branches recursively. Caveat: this may
337
 * be affected, and modifying the result may affect this. 
338
 * @return a flow info that merges the possible paths of execution described by
339
 * 			this
340
 */
341
abstract public UnconditionalFlowInfo unconditionalInits();
342
343
/**
344
 * Return a new flow info that holds the same information as this would after
345
 * a call to {@link #unconditionalInits() unconditionalInits}, but leaving 
346
 * this info unaffected. Side effects on the result might affect this though 
347
 * (consider it as read only).
348
 * @return a flow info carrying this unconditional flow info
349
 */
350
abstract public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect();
186
}
351
}
(-)compiler/org/eclipse/jdt/internal/compiler/flow/InsideSubRoutineFlowContext.java (-8 / +9 lines)
Lines 47-59 Link Here
47
		return (SubRoutineStatement)associatedNode;
47
		return (SubRoutineStatement)associatedNode;
48
	}
48
	}
49
	
49
	
50
	public void recordReturnFrom(FlowInfo flowInfo) {
50
public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
51
51
	if (!flowInfo.isReachable()) {
52
		if (!flowInfo.isReachable()) return; 
52
		return; 
53
		if (initsOnReturn == FlowInfo.DEAD_END) {
54
			initsOnReturn = flowInfo.copy().unconditionalInits();
55
		} else {
56
			initsOnReturn = initsOnReturn.mergedWith(flowInfo.copy().unconditionalInits());
57
		}
58
	}
53
	}
54
	if (initsOnReturn == FlowInfo.DEAD_END) {
55
		initsOnReturn = (UnconditionalFlowInfo) flowInfo.copy();
56
	} else {
57
		initsOnReturn = initsOnReturn.mergedWith(flowInfo);
58
	}
59
}
59
}
60
}
(-)compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java (-65 / +226 lines)
Lines 28-109 Link Here
28
	
28
	
29
	public Label continueLabel;
29
	public Label continueLabel;
30
	public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END;
30
	public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END;
31
	private UnconditionalFlowInfo upstreamNullFlowInfo;
32
	private LoopingFlowContext innerFlowContexts[] = null;
33
	private UnconditionalFlowInfo innerFlowInfos[] = null;
34
	private int innerFlowContextsNb = 0;
35
	
31
	Reference finalAssignments[];
36
	Reference finalAssignments[];
32
	VariableBinding finalVariables[];
37
	VariableBinding finalVariables[];
33
	int assignCount = 0;
38
	int assignCount = 0;
34
	
39
	
40
	LocalVariableBinding[] nullLocals;
35
	Expression[] nullReferences;
41
	Expression[] nullReferences;
36
	int[] nullStatus;
42
	int[] nullCheckTypes;
37
	int nullCount;
43
	int nullCount;
38
	
44
	
39
	Scope associatedScope;
45
	Scope associatedScope;
40
	
46
	
41
	public LoopingFlowContext(
47
	public LoopingFlowContext(
42
		FlowContext parent,
48
		FlowContext parent,
49
		FlowInfo upstreamNullFlowInfo,
43
		ASTNode associatedNode,
50
		ASTNode associatedNode,
44
		Label breakLabel,
51
		Label breakLabel,
45
		Label continueLabel,
52
		Label continueLabel,
46
		Scope associatedScope) {
53
		Scope associatedScope) {
47
		super(parent, associatedNode, breakLabel);
54
		super(parent, associatedNode, breakLabel);
55
		preemptNullDiagnostic = true; 
56
			// children will defer to this, which may defer to its own parent 
48
		this.continueLabel = continueLabel;
57
		this.continueLabel = continueLabel;
49
		this.associatedScope = associatedScope;
58
		this.associatedScope = associatedScope;
59
		this.upstreamNullFlowInfo = upstreamNullFlowInfo.unconditionalCopy();
50
	}
60
	}
51
	
61
52
	public void complainOnDeferredChecks(BlockScope scope, FlowInfo flowInfo) {
62
/**
53
		
63
 * Perform deferred checks relative to final variables duplicate initialization 
54
		// complain on final assignments in loops
64
 * of lack of initialization.
55
		for (int i = 0; i < assignCount; i++) {
65
 * @param scope the scope to which this context is associated
56
			VariableBinding variable = finalVariables[i];
66
 * @param flowInfo the flow info against which checks must be performed
57
			if (variable == null) continue;
67
 */
58
			boolean complained = false; // remember if have complained on this final assignment
68
public void complainOnDeferredFinalChecks(BlockScope scope, FlowInfo flowInfo) {
59
			if (variable instanceof FieldBinding) {
69
	// complain on final assignments in loops
60
				if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) {
70
	for (int i = 0; i < assignCount; i++) {
61
					complained = true;
71
		VariableBinding variable = finalVariables[i];
62
					scope.problemReporter().duplicateInitializationOfBlankFinalField(
72
		if (variable == null) continue;
63
						(FieldBinding) variable,
73
		boolean complained = false; // remember if have complained on this final assignment
64
						finalAssignments[i]);
74
		if (variable instanceof FieldBinding) {
65
				}
75
			if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) {
66
			} else {
76
				complained = true;
67
				if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
77
				scope.problemReporter().duplicateInitializationOfBlankFinalField(
68
					complained = true;
78
					(FieldBinding) variable,
69
					scope.problemReporter().duplicateInitializationOfFinalLocal(
79
					finalAssignments[i]);
70
						(LocalVariableBinding) variable,
71
						finalAssignments[i]);
72
				}
73
			}
80
			}
74
			// any reference reported at this level is removed from the parent context where it 
81
		} else {
75
			// could also be reported again
82
			if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
76
			if (complained) {
83
				complained = true;
77
				FlowContext context = parent;
84
				scope.problemReporter().duplicateInitializationOfFinalLocal(
78
				while (context != null) {
85
					(LocalVariableBinding) variable,
79
					context.removeFinalAssignmentIfAny(finalAssignments[i]);
86
					finalAssignments[i]);
80
					context = context.parent;
81
				}
82
			}
87
			}
83
		}
88
		}
84
		// check inconsistent null checks
89
		// any reference reported at this level is removed from the parent context where it 
85
		for (int i = 0; i < nullCount; i++) {
90
		// could also be reported again
86
			Expression expression = nullReferences[i];
91
		if (complained) {
87
			if (expression == null) continue;
92
			FlowContext context = parent;
93
			while (context != null) {
94
				context.removeFinalAssignmentIfAny(finalAssignments[i]);
95
				context = context.parent;
96
			}
97
		}
98
	}
99
}
100
101
/**
102
 * Perform deferred checks relative to the null status of local variables.
103
 * @param scope the scope to which this context is associated
104
 * @param flowInfo the flow info against which checks must be performed
105
 */
106
public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo flowInfo) {
107
	for (int i = 0 ; i < this.innerFlowContextsNb ; i++) {
108
		this.upstreamNullFlowInfo.
109
			addPotentialNullInfoFrom(
110
				this.innerFlowContexts[i].upstreamNullFlowInfo).
111
			addPotentialNullInfoFrom(this.innerFlowInfos[i]);
112
	}
113
	this.innerFlowContextsNb = 0;
114
	flowInfo = this.upstreamNullFlowInfo.
115
		addPotentialNullInfoFrom(
116
			flowInfo.unconditionalInitsWithoutSideEffect());
117
	if (this.deferNullDiagnostic) {
118
		// check only immutable null checks on innermost looping context
119
		for (int i = 0; i < this.nullCount; i++) {
120
			LocalVariableBinding local = this.nullLocals[i];
121
			Expression expression = this.nullReferences[i];
88
			// final local variable
122
			// final local variable
89
			LocalVariableBinding local = expression.localVariableBinding();
123
			switch (this.nullCheckTypes[i]) {
90
			switch (nullStatus[i]) {
124
				case CAN_ONLY_NULL_NON_NULL :
91
				case FlowInfo.NULL :
125
					if (flowInfo.isDefinitelyNonNull(local)) {
126
						this.nullReferences[i] = null;
127
						scope.problemReporter().localVariableCannotBeNull(local, expression);
128
						continue;
129
					}
130
				case CAN_ONLY_NULL :
92
					if (flowInfo.isDefinitelyNull(local)) {
131
					if (flowInfo.isDefinitelyNull(local)) {
93
						nullReferences[i] = null;
132
						this.nullReferences[i] = null;
94
						this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo);
133
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
134
						continue;
95
					}
135
					}
96
					break;
136
					break;
97
				case FlowInfo.NON_NULL :
137
				case MAY_NULL:
138
					if (flowInfo.isDefinitelyNull(local)) {
139
						this.nullReferences[i] = null;
140
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
141
						continue;
142
					}
143
					break;
144
				default:
145
					// never happens	
146
			}
147
			this.parent.recordUsingNullReference(scope, local, expression, 
148
					this.nullCheckTypes[i], flowInfo);
149
		}
150
	}
151
	else {
152
		// check inconsistent null checks on outermost looping context
153
		for (int i = 0; i < this.nullCount; i++) {
154
			Expression expression = this.nullReferences[i];
155
			// final local variable
156
			LocalVariableBinding local = this.nullLocals[i];
157
			switch (this.nullCheckTypes[i]) {
158
				case CAN_ONLY_NULL_NON_NULL :
98
					if (flowInfo.isDefinitelyNonNull(local)) {
159
					if (flowInfo.isDefinitelyNonNull(local)) {
99
						nullReferences[i] = null;
160
						this.nullReferences[i] = null;
100
						this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo);
161
						scope.problemReporter().localVariableCannotBeNull(local, expression);
162
						continue;
163
					}
164
				case CAN_ONLY_NULL :
165
					if (flowInfo.isDefinitelyNull(local)) {
166
						this.nullReferences[i] = null;
167
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
168
						continue;
101
					}
169
					}
102
					break;
170
					break;
171
				case MAY_NULL:
172
					if (flowInfo.isDefinitelyNull(local)) {
173
						this.nullReferences[i] = null;
174
						scope.problemReporter().localVariableCanOnlyBeNull(local, expression);
175
						continue;
176
					}
177
					if (flowInfo.isPotentiallyNull(local)) {
178
						this.nullReferences[i] = null;
179
						scope.problemReporter().localVariableMayBeNull(local, expression);
180
						continue;
181
					}
182
					break;
183
				default:
184
					// never happens	
103
			}
185
			}
104
		}		
186
		}
105
	}
187
	}
106
188
}
189
	
107
	public Label continueLabel() {
190
	public Label continueLabel() {
108
		return continueLabel;
191
		return continueLabel;
109
	}
192
	}
Lines 125-139 Link Here
125
		return initsOnContinue != FlowInfo.DEAD_END;
208
		return initsOnContinue != FlowInfo.DEAD_END;
126
	}
209
	}
127
210
128
	public void recordContinueFrom(FlowInfo flowInfo) {
211
public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) {
129
212
	if (!flowInfo.isReachable()) {
130
		if (!flowInfo.isReachable()) return;
213
		return;
131
		if (initsOnContinue == FlowInfo.DEAD_END) {
214
	}
132
			initsOnContinue = flowInfo.copy().unconditionalInits();
215
	if (initsOnContinue.isReachable()) {
133
		} else {
216
		initsOnContinue = initsOnContinue.
134
			initsOnContinue = initsOnContinue.mergedWith(flowInfo.copy().unconditionalInits());
217
			mergedWith(flowInfo.unconditionalInitsWithoutSideEffect());
218
	} 
219
	else {
220
		initsOnContinue = flowInfo.unconditionalCopy();
221
	}
222
	FlowContext inner = innerFlowContext;
223
	while (inner != this && !(inner instanceof LoopingFlowContext)) {
224
		inner = inner.parent;
225
	}
226
	if (inner == this) {
227
		this.upstreamNullFlowInfo.
228
			addPotentialNullInfoFrom(
229
				flowInfo.unconditionalInitsWithoutSideEffect());
230
	}
231
	else {
232
		int length = 0;
233
		if (this.innerFlowContexts == null) {
234
			this.innerFlowContexts = new LoopingFlowContext[5];
235
			this.innerFlowInfos = new UnconditionalFlowInfo[5];
135
		}
236
		}
237
		else if (this.innerFlowContextsNb == 
238
				(length = this.innerFlowContexts.length) - 1) {
239
			System.arraycopy(this.innerFlowContexts, 0, 
240
				(this.innerFlowContexts = new LoopingFlowContext[length + 5]), 
241
				0, length);
242
			System.arraycopy(this.innerFlowInfos, 0, 
243
				(this.innerFlowInfos= new UnconditionalFlowInfo[length + 5]), 
244
				0, length);
245
		}
246
		this.innerFlowContexts[this.innerFlowContextsNb] = (LoopingFlowContext) inner;
247
		this.innerFlowInfos[this.innerFlowContextsNb++] = 
248
			flowInfo.unconditionalInitsWithoutSideEffect();
136
	}
249
	}
250
}
137
251
138
	protected boolean recordFinalAssignment(
252
	protected boolean recordFinalAssignment(
139
		VariableBinding binding,
253
		VariableBinding binding,
Lines 170-189 Link Here
170
		return true;
284
		return true;
171
	}
285
	}
172
286
173
	protected boolean recordNullReference(Expression expression, int status) {
287
protected void recordNullReference(LocalVariableBinding local, 
174
		if (nullCount == 0) {
288
	Expression expression, int status) {
175
			nullReferences = new Expression[5];
289
	if (nullCount == 0) {
176
			nullStatus = new int[5];
290
		nullLocals = new LocalVariableBinding[5];
177
		} else {
291
		nullReferences = new Expression[5];
178
			if (nullCount == nullReferences.length) {
292
		nullCheckTypes = new int[5];
179
				System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount);
293
	} 
180
				System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount);
294
	else if (nullCount == nullLocals.length) {
295
		System.arraycopy(nullLocals, 0, 
296
			nullLocals = new LocalVariableBinding[nullCount * 2], 0, nullCount);
297
		System.arraycopy(nullReferences, 0, 
298
			nullReferences = new Expression[nullCount * 2], 0, nullCount);
299
		System.arraycopy(nullCheckTypes, 0, 
300
			nullCheckTypes = new int[nullCount * 2], 0, nullCount);
301
	}
302
	nullLocals[nullCount] = local;
303
	nullReferences[nullCount] = expression;
304
	nullCheckTypes[nullCount++] = status;
305
}
306
	
307
public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
308
		Expression reference, int checkType, FlowInfo flowInfo) {
309
	if (!flowInfo.isReachable() || flowInfo.isDefinitelyUnknown(local)) {
310
		return;
311
	}
312
	switch (checkType) {
313
		case CAN_ONLY_NULL_NON_NULL :
314
		case CAN_ONLY_NULL:
315
			if (flowInfo.isDefinitelyNonNull(local)) {
316
				if (checkType == CAN_ONLY_NULL_NON_NULL) {
317
					scope.problemReporter().localVariableCannotBeNull(local, reference);
318
				}
319
				return;
181
			}
320
			}
182
		}
321
			if (flowInfo.isDefinitelyNull(local)) {
183
		nullReferences[nullCount] = expression;
322
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
184
		nullStatus[nullCount++] = status;
323
				return;
185
		return true;
324
			}
186
	}	
325
			if (flowInfo.isPotentiallyUnknown(local)) {
326
				return;
327
			}
328
			recordNullReference(local, reference, checkType);
329
			return;
330
		case MAY_NULL :
331
			if (flowInfo.isDefinitelyNonNull(local)) {
332
				return;
333
			}
334
			if (flowInfo.isDefinitelyNull(local)) {
335
				scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
336
				return;
337
			}
338
			if (flowInfo.isPotentiallyNull(local)) {
339
				scope.problemReporter().localVariableMayBeNull(local, reference);
340
				return;
341
			}
342
			recordNullReference(local, reference, checkType);
343
			return;
344
		default:
345
			// never happens
346
	}
347
}
187
	
348
	
188
	void removeFinalAssignmentIfAny(Reference reference) {
349
	void removeFinalAssignmentIfAny(Reference reference) {
189
		for (int i = 0; i < assignCount; i++) {
350
		for (int i = 0; i < assignCount; i++) {
(-)compiler/org/eclipse/jdt/internal/compiler/flow/SwitchFlowContext.java (-4 / +5 lines)
Lines 42-51 Link Here
42
}
42
}
43
43
44
public void recordBreakFrom(FlowInfo flowInfo) {
44
public void recordBreakFrom(FlowInfo flowInfo) {
45
	if (initsOnBreak == FlowInfo.DEAD_END) {
45
	if (initsOnBreak.isReachable()) {
46
		initsOnBreak = flowInfo.copy().unconditionalInits();
46
		initsOnBreak = initsOnBreak.mergedWith(flowInfo.unconditionalInits());
47
	} else {
47
	} 
48
		initsOnBreak = initsOnBreak.mergedWith(flowInfo.copy().unconditionalInits());
48
	else {
49
		initsOnBreak = flowInfo.unconditionalCopy();
49
	}
50
	}
50
}
51
}
51
}
52
}
(-)compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java (-715 / +1434 lines)
Lines 14-19 Link Here
14
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
14
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
15
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
15
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
16
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
16
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
17
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
18
17
19
18
/**
20
/**
19
 * Record initialization status during definite assignment analysis
21
 * Record initialization status during definite assignment analysis
Lines 22-828 Link Here
22
 */
24
 */
23
public class UnconditionalFlowInfo extends FlowInfo {
25
public class UnconditionalFlowInfo extends FlowInfo {
24
26
25
	
26
	public long definiteInits;
27
	public long definiteInits;
27
	public long potentialInits;
28
	public long potentialInits;
28
	public long extraDefiniteInits[];
29
	public long extraPotentialInits[];
30
	
29
	
31
	public long definiteNulls;
30
	public long nullAssignmentStatusBit1;
32
	public long definiteNonNulls;
31
	public long nullAssignmentStatusBit2;
33
	public long extraDefiniteNulls[];
32
	// 0 0 is potential (bit 1 is leftmost here)
34
	public long extraDefiniteNonNulls[];
33
	// 1 0 is assigned
35
34
	// 0 1 is protected null (aka if (o == null) { // here o protected null...)
36
	public int reachMode; // by default
35
	// 1 1 is protected non null
36
	public long nullAssignmentValueBit1;
37
	public long nullAssignmentValueBit2;
38
	// information only relevant for potential and assigned
39
	// 0 0 is start -- nothing known at all
40
	// 0 1 is assigned non null or potential anything but null
41
	// 1 0 is assigned null or potential null
42
	// 1 1 is potential null and potential anything but null or definite unknown
43
44
	public static final int extraLength = 6;
45
	public long extra[][];
46
		// extra bit fields for larger numbers of fields/variables
47
		// extra[0] holds definiteInits values, extra[1] potentialInits, etc.
48
		// lifecycle is extra == null or else all extra[]'s are allocated
49
		// arrays which have the same size
37
50
38
	public int maxFieldCount;
51
	public int maxFieldCount; // limit between fields and locals
39
	
52
	
40
	// Constants
53
	// Constants
41
	public static final int BitCacheSize = 64; // 64 bits in a long.
54
	public static final int BitCacheSize = 64; // 64 bits in a long.
42
55
43
	UnconditionalFlowInfo() {
56
public FlowInfo addInitializationsFrom(FlowInfo inits) {
44
		this.reachMode = REACHABLE;
57
	if (this == DEAD_END)
45
	}
58
		return this;
46
59
	if (inits == DEAD_END)
47
	// unions of both sets of initialization - used for try/finally
60
		return this;
48
	public FlowInfo addInitializationsFrom(FlowInfo inits) {
61
	UnconditionalFlowInfo otherInits = inits.unconditionalInits();		
49
62
50
		if (this == DEAD_END)
63
	// union of definitely assigned variables, 
51
			return this;
64
	this.definiteInits |= otherInits.definiteInits;
52
65
	// union of potentially set ones
53
		UnconditionalFlowInfo otherInits = inits.unconditionalInits();	
66
	this.potentialInits |= otherInits.potentialInits;
54
		if (otherInits == DEAD_END)
67
	// combine null information
55
			return this;
68
	// note: we may have both forms of protection (null and non null) 
56
			
69
	// coming with otherInits, because of loops
57
		// union of definitely assigned variables, 
70
	boolean considerNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0;
58
		definiteInits |= otherInits.definiteInits;
71
	long a1, na1, a2, na2, a3, a4, na4, b1, b2, nb2, b3, nb3, b4, nb4;
59
		// union of potentially set ones
72
	if (considerNulls) {
60
		potentialInits |= otherInits.potentialInits;
73
		if ((this.tagBits & NULL_FLAG_MASK) == 0) {
61
	
74
			this.nullAssignmentStatusBit1 = otherInits.nullAssignmentStatusBit1;
62
		// union of definitely null variables, 
75
			this.nullAssignmentStatusBit2 = otherInits.nullAssignmentStatusBit2;
63
		definiteNulls = (definiteNulls | otherInits.definiteNulls) & ~otherInits.definiteNonNulls;
76
			this.nullAssignmentValueBit1 = otherInits.nullAssignmentValueBit1;
64
		// union of definitely non null variables,
77
			this.nullAssignmentValueBit2 = otherInits.nullAssignmentValueBit2;
65
		definiteNonNulls = (definiteNonNulls | otherInits.definiteNonNulls) & ~otherInits.definiteNulls;
78
		}
66
		// fix-up null/non-null infos since cannot overlap: <defN1:0,defNoN1:1>  + <defN2:1,defNoN2:0>  --> <defN:0,defNon:0>
79
		else {
67
80
			this.nullAssignmentStatusBit1 =
68
		// treating extra storage
81
				(b1 = otherInits.nullAssignmentStatusBit1) |
69
		if (extraDefiniteInits != null) {
82
				(a1	 = this.nullAssignmentStatusBit1) & 
70
			if (otherInits.extraDefiniteInits != null) {
83
					((nb2 = ~(b2 = otherInits.nullAssignmentStatusBit2)) &
84
						(nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) &
85
						((nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) |
86
							((a2 = this.nullAssignmentStatusBit2) ^ 
87
								(a4 = this.nullAssignmentValueBit2))) | 
88
					nb4 & (na2 = ~a2) & (na4 = ~a4));
89
			this.nullAssignmentStatusBit2 =
90
				b1 & b2 |
91
				~b1 & (((na1 = ~a1) | a4) & b2 |
92
						a2 & (b2 |
93
							a1 & (na4 = ~a4) & nb2 & nb3 |
94
							(~(a3 = this.nullAssignmentValueBit1) & nb3 | na1 & na4) & nb4));
95
			this.nullAssignmentValueBit1 = 
96
				nb2 & b3 |
97
				~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) |
98
						a1 & na2 & a3 & nb2 |
99
						(a1 | a2 | na4) & b3);
100
			this.nullAssignmentValueBit2 =
101
				b4 |
102
				a4 & (nb2 & nb3 | ~(b1 ^ b2));
103
		}
104
		this.tagBits |= NULL_FLAG_MASK; // in all cases - avoid forgetting extras
105
	}
106
	// treating extra storage
107
	if (this.extra != null || otherInits.extra != null) {
108
		int mergeLimit = 0, copyLimit = 0;
109
		if (this.extra != null) {
110
			if (otherInits.extra != null) {
71
				// both sides have extra storage
111
				// both sides have extra storage
72
				int i = 0, length, otherLength;
112
				int length, otherLength;
73
				if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
113
				if ((length = this.extra[0].length) < 
114
						(otherLength = otherInits.extra[0].length)) {
74
					// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
115
					// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
75
					System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
116
					for (int j = 0; j < extraLength; j++) {
76
					System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
117
						System.arraycopy(this.extra[j], 0, 
77
					System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, length);
118
							(this.extra[j] = new long[otherLength]), 0, length);
78
					System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, length);					
79
					for (; i < length; i++) {
80
						extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
81
						extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
82
						extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i];
83
						extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i];
84
					}
85
					for (; i < otherLength; i++) {
86
						extraPotentialInits[i] = otherInits.extraPotentialInits[i];
87
					}
119
					}
120
					mergeLimit = length;
121
					copyLimit = otherLength;
88
				} else {
122
				} else {
89
					// current storage is longer
123
					// current storage is longer
90
					for (; i < otherLength; i++) {
124
					mergeLimit = otherLength;
91
						extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
125
				}
92
						extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
126
			} 
93
						extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i];
127
		} 
94
						extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i];
128
		else if (otherInits.extra != null) {
95
					}
129
			// no storage here, but other has extra storage.
96
					for (; i < length; i++) {
130
			// shortcut regular copy because array copy is better
97
						extraDefiniteInits[i] = 0;
131
			int otherLength;
98
						extraDefiniteNulls[i] = 0;
132
			this.extra = new long[extraLength][];
99
						extraDefiniteNonNulls[i] = 0;
133
			System.arraycopy(otherInits.extra[0], 0, 
134
				(this.extra[0] = new long[otherLength = 
135
					otherInits.extra[0].length]), 0, otherLength);			
136
			System.arraycopy(otherInits.extra[1], 0, 
137
				(this.extra[1] = new long[otherLength]), 0, otherLength);
138
			if (considerNulls) {
139
				for (int j = 2; j < extraLength; j++) {
140
					System.arraycopy(otherInits.extra[j], 0, 
141
						(this.extra[j] = new long[otherLength]), 0, otherLength);
142
				}
143
			}
144
			else {
145
				for (int j = 2; j < extraLength; j++) {
146
					this.extra[j] = new long[otherLength];			
147
				}
148
			}
149
		}
150
		int i = 0;
151
		for (; i < mergeLimit; i++) {
152
			this.extra[0][i] |= otherInits.extra[0][i];
153
			this.extra[1][i] |= otherInits.extra[1][i];
154
			if (considerNulls) { // could consider pushing the test outside the loop
155
				if (this.extra[2][i] == 0 &&
156
						this.extra[3][i] == 0 &&
157
						this.extra[4][i] == 0 &&
158
						this.extra[5][i] == 0) {
159
					for (int j = 2; j < extraLength; j++) {
160
						this.extra[j][i] = otherInits.extra[j][i];
100
					}
161
					}
101
				}
162
				}
102
			} else {
163
				else {
103
				// no extra storage on otherInits
164
					this.extra[2][i] =
165
						(b1 = otherInits.extra[2][i]) |
166
						(a1	 = this.extra[2][i]) & 
167
							((nb2 = ~(b2 = otherInits.extra[3][i])) &
168
								(nb3 = ~(b3 = otherInits.extra[4][i])) &
169
								((nb4 = ~(b4 = otherInits.extra[5][i])) |
170
									((a2 = this.extra[3][i]) ^ 
171
										(a4 = this.extra[5][i]))) | 
172
							nb4 & (na2 = ~a2) & (na4 = ~a4));
173
					this.extra[3][i] =
174
						b1 & b2 |
175
						~b1 & (((na1 = ~a1) | a4) & b2 |
176
								a2 & (b2 |
177
									a1 & (na4 = ~a4) & nb2 & nb3 |
178
									(~(a3 = this.extra[4][i]) & nb3 | na1 & na4) & nb4));
179
					this.extra[4][i] = 
180
						nb2 & b3 |
181
						~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) |
182
								a1 & na2 & a3 & nb2 |
183
								(a1 | a2 | na4) & b3);
184
					this.extra[5][i] =
185
						b4 |
186
						a4 & (nb2 & nb3 | ~(b1 ^ b2));
187
				}
104
			}
188
			}
105
		} else
189
		}
106
			if (otherInits.extraDefiniteInits != null) {
190
		for (; i < copyLimit; i++) {
107
				// no storage here, but other has extra storage.
191
			this.extra[0][i] = otherInits.extra[0][i];
108
				int otherLength;
192
			this.extra[1][i] = otherInits.extra[1][i];
109
				System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength);			
193
			if (considerNulls) {
110
				System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
194
				for (int j = 2; j < extraLength; j++) {
111
				System.arraycopy(otherInits.extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, otherLength);			
195
					this.extra[j][i] = otherInits.extra[j][i];
112
				System.arraycopy(otherInits.extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, otherLength);			
196
				}
113
			}
197
			}
114
		return this;
198
		}
115
	}
199
	}
200
	return this;
201
}
116
202
117
	// unions of both sets of initialization - used for try/finally
203
public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
118
	public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
204
	if (this == DEAD_END){
119
	
205
		return this;
120
		if (this == DEAD_END){
206
	}
121
			return this;
207
	if (inits == DEAD_END){
208
		return this;
209
	}
210
	UnconditionalFlowInfo otherInits = inits.unconditionalInits();
211
	// union of potentially set ones
212
	this.potentialInits |= otherInits.potentialInits;
213
	// treating extra storage
214
	if (this.extra != null) {
215
		if (otherInits.extra != null) {
216
			// both sides have extra storage
217
			int i = 0, length, otherLength;
218
			if ((length = this.extra[0].length) < (otherLength = otherInits.extra[0].length)) {
219
				// current storage is shorter -> grow current
220
				for (int j = 0; j < extraLength; j++) {
221
					System.arraycopy(this.extra[j], 0, 
222
						(this.extra[j] = new long[otherLength]), 0, length);
223
				}
224
				for (; i < length; i++) {
225
					this.extra[1][i] |= otherInits.extra[1][i];
226
				}
227
				for (; i < otherLength; i++) {
228
					this.extra[1][i] = otherInits.extra[1][i];
229
				}
230
			} 
231
			else {
232
				// current storage is longer
233
				for (; i < otherLength; i++) {
234
					this.extra[1][i] |= otherInits.extra[1][i];
235
				}
236
			}
237
		}
238
	} 
239
	else if (otherInits.extra != null) {
240
		// no storage here, but other has extra storage.
241
		int otherLength = otherInits.extra[0].length;
242
		this.extra = new long[extraLength][];
243
		for (int j = 0; j < extraLength; j++) {
244
			this.extra[j] = new long[otherLength];			
122
		}
245
		}
246
		System.arraycopy(otherInits.extra[1], 0, this.extra[1], 0, 
247
			otherLength);
248
	}
249
	this.addPotentialNullInfoFrom(otherInits);
250
	return this;
251
}
123
252
124
		UnconditionalFlowInfo otherInits = inits.unconditionalInits();
253
/**
125
		if (otherInits == DEAD_END){
254
 * Compose other inits over this flow info, then return this. The operation
126
			return this;
255
 * semantics are to wave into this flow info the consequences upon null 
127
		}
256
 * information of a possible path into the operations that resulted into 
128
		// union of potentially set ones
257
 * otherInits. The fact that this path may be left unexecuted under peculiar 
129
		this.potentialInits |= otherInits.potentialInits;
258
 * conditions results into less specific results than 
130
		// also merge null check information (affected by potential inits)
259
 * {@link #addInitializationsFrom(FlowInfo) addInitializationsFrom}; moreover,
131
		this.definiteNulls &= otherInits.definiteNulls;
260
 * only the null information is affected.
132
		this.definiteNonNulls &= otherInits.definiteNonNulls;
261
 * @param otherInits other null inits to compose over this
133
	
262
 * @return this, modified according to otherInits information
134
		// treating extra storage
263
 */
135
		if (this.extraDefiniteInits != null) {
264
public UnconditionalFlowInfo addPotentialNullInfoFrom(
136
			if (otherInits.extraDefiniteInits != null) {
265
		UnconditionalFlowInfo otherInits) {
137
				// both sides have extra storage
266
	if ((this.tagBits & UNREACHABLE) != 0 ||
138
				int i = 0, length, otherLength;
267
			(otherInits.tagBits & UNREACHABLE) != 0 ||
139
				if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
268
			(otherInits.tagBits & NULL_FLAG_MASK) == 0) {
140
					// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
269
		return this;
141
					System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
270
	}
142
					System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
271
	// if we get here, otherInits has some null info
143
					System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length);
272
	boolean thisHasNulls = (this.tagBits & NULL_FLAG_MASK) != 0;
144
					System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length);
273
	if (thisHasNulls) {
145
					while (i < length) {
274
		long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4;
146
						this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
275
		this.nullAssignmentStatusBit1 =
147
						this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
276
			((a1 = this.nullAssignmentStatusBit1) &
148
						this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
277
					(na4 = ~(a4 = this.nullAssignmentValueBit2)) &	
278
					((na3 = ~(a3 = this.nullAssignmentValueBit1)) | 
279
							(a2 = this.nullAssignmentStatusBit2)) | 
280
							a2 & na3 &	a4) & 
281
					(nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) &
282
					((b2 = otherInits.nullAssignmentStatusBit2) | 
283
					(nb4 = ~(b4 = otherInits.nullAssignmentValueBit2))) |
284
			a1 & (na2 = ~a2) & 
285
				(a4 & ((nb1 = ~(b1 = otherInits.nullAssignmentStatusBit1)) & 
286
						nb3 | b1 &
287
						(b4 | b2)) |
288
				na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | 
289
						b1 & nb4 & (nb2 | nb3)));
290
		this.nullAssignmentStatusBit2 =
291
			a2 & (~a1 & na4 & nb4 |
292
					a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) |
293
										b1 & (nb4 |b2 & b4)));
294
		this.nullAssignmentValueBit1 =
295
			a3 |
296
			b1 & nb2 & nb4 |
297
			nb1 & b3 |
298
			a1 & na2 & (b1 & b3 | nb1 & b4);
299
//			b1 & (~b2 & ~b4 | a1 & ~a2 & b3) |
300
//			~b1 & (b3 | a1 & ~a2 & b4); -- same op nb
301
		this.nullAssignmentValueBit2 =
302
			a4 & (na2 | a2 & na3) |
303
			b4 & (nb2 | b2 & nb3);
304
		// extra storage management
305
		if (otherInits.extra != null) {
306
			int mergeLimit = 0, copyLimit = 0;
307
			int otherLength = otherInits.extra[0].length;
308
			if (this.extra == null) {
309
				this.extra = new long[extraLength][];
310
				for (int j = 0; j < extraLength; j++) {
311
					this.extra[j] = new long[otherLength];
312
				}
313
				copyLimit = otherLength;
314
			}
315
			else {
316
				mergeLimit = otherLength;
317
				if (mergeLimit > this.extra[0].length) {
318
					copyLimit = mergeLimit;
319
					mergeLimit = this.extra[0].length;
320
					for (int j = 0; j < extraLength; j++) {
321
						System.arraycopy(this.extra[j], 0,
322
								this.extra[j] = new long[otherLength], 0,
323
								mergeLimit);
149
					}
324
					}
150
					while (i < otherLength) {
325
				}
151
						this.extraPotentialInits[i] = otherInits.extraPotentialInits[i];
326
				int i;
152
						this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
327
				for (i = 0; i < mergeLimit; i++) {
153
						this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
328
					this.extra[2][i] =
329
						((a1 = this.extra[2][i]) &
330
								(na4 = ~(a4 = this.extra[5][i])) &	
331
								((na3 = ~(a3 = this.extra[4][i])) | 
332
										(a2 = this.extra[3][i])) | 
333
										a2 & na3 &	a4) & 
334
								(nb3 = ~(b3 = otherInits.extra[4][i])) &
335
								((b2 = otherInits.extra[3][i]) | 
336
								(nb4 = ~(b4 = otherInits.extra[5][i]))) |
337
						a1 & (na2 = ~a2) & 
338
							(a4 & ((nb1 = ~(b1 = otherInits.extra[2][i])) & 
339
									nb3 | b1 &
340
									(b4 | b2)) |
341
							na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) | 
342
									b1 & nb4 & (nb2 | nb3)));
343
					this.extra[3][i] =
344
						a2 & (~a1 & na4 & nb4 |
345
								a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) |
346
													b1 & (nb4 |b2 & b4)));
347
					this.extra[4][i] =
348
						a3 |
349
						b1 & nb2 & nb4 |
350
						nb1 & b3 |
351
						a1 & na2 & (b1 & b3 | nb1 & b4);
352
					this.extra[5][i] =
353
						a4 & (na2 | a2 & na3) |
354
						b4 & (nb2 | b2 & nb3);
355
				}
356
				for (; i < copyLimit; i++) {
357
					if (otherInits.extra[4][i] != 0 ||
358
						otherInits.extra[5][i] != 0) {
359
						this.tagBits |= NULL_FLAG_MASK; 
360
						this.extra[4][i] = 
361
							otherInits.extra[4][i] &
362
							~(otherInits.extra[2][i] &
363
							  ~otherInits.extra[3][i] &
364
							  otherInits.extra[5][i]);
365
						this.extra[5][i] = 
366
							otherInits.extra[5][i];
154
					}
367
					}
155
				} else {
368
				}
156
					// current storage is longer
369
			}
157
					while (i < otherLength) {
370
		}
158
						this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
371
	}
159
						this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
372
	else {
160
						this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
373
		if (otherInits.nullAssignmentValueBit1 != 0 ||
374
			otherInits.nullAssignmentValueBit2 != 0) {
375
			// add potential values
376
			this.nullAssignmentValueBit1 = 
377
				otherInits.nullAssignmentValueBit1 & 
378
					~(otherInits.nullAssignmentStatusBit1 &
379
					  ~otherInits.nullAssignmentStatusBit2 &
380
					  otherInits.nullAssignmentValueBit2); // exclude assigned unknown
381
			this.nullAssignmentValueBit2 = 
382
				otherInits.nullAssignmentValueBit2;
383
			thisHasNulls = 
384
				this.nullAssignmentValueBit1 != 0 ||
385
				this.nullAssignmentValueBit2 != 0;
386
		}
387
		// extra storage management
388
		if (otherInits.extra != null) {
389
			int mergeLimit = 0, copyLimit = 0;
390
			int otherLength = otherInits.extra[0].length;
391
			if (this.extra == null) {
392
				copyLimit = otherLength; 
393
					// cannot happen when called from addPotentialInitializationsFrom
394
				this.extra = new long[extraLength][];
395
				for (int j = 0; j < extraLength; j++) {
396
					this.extra[j] = new long[otherLength];
397
				}
398
			}
399
			else {
400
				mergeLimit = otherLength;
401
				if (mergeLimit > this.extra[0].length) {
402
					copyLimit = mergeLimit;
403
					mergeLimit = this.extra[0].length;
404
					System.arraycopy(this.extra[0], 0,
405
							this.extra[0] = new long[otherLength], 0,
406
							mergeLimit);
407
					System.arraycopy(this.extra[1], 0,
408
							this.extra[1] = new long[otherLength], 0,
409
							mergeLimit);
410
					for (int j = 2; j < extraLength; j++) {
411
						this.extra[j] = new long[otherLength];
161
					}
412
					}
162
				}
413
				}
163
			}
414
			}
164
		} else
415
			int i;
165
			if (otherInits.extraDefiniteInits != null) {
416
			for (i = 0; i < mergeLimit; i++) {
166
				// no storage here, but other has extra storage.
417
				if (otherInits.extra[4][i] != 0 ||
167
				int otherLength;
418
					otherInits.extra[5][i] != 0) {
168
				this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];			
419
					this.extra[4][i] |= 
169
				System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
420
						otherInits.extra[4][i] &
170
				this.extraDefiniteNulls = new long[otherLength];			
421
						~(otherInits.extra[2][i] &
171
				this.extraDefiniteNonNulls = new long[otherLength];			
422
						  ~otherInits.extra[3][i] &
423
						  otherInits.extra[5][i]);
424
					this.extra[5][i] |= 
425
						otherInits.extra[5][i];
426
					thisHasNulls = thisHasNulls ||
427
						this.extra[4][i] != 0 ||
428
						this.extra[5][i] != 0;
429
				}
430
			}
431
			for (; i < copyLimit; i++) {
432
				if (otherInits.extra[4][i] != 0 ||
433
					otherInits.extra[5][i] != 0) {
434
					this.extra[4][i] = 
435
						otherInits.extra[4][i] &
436
						~(otherInits.extra[2][i] &
437
						  ~otherInits.extra[3][i] &
438
						  otherInits.extra[5][i]);
439
					this.extra[5][i] = 
440
						otherInits.extra[5][i];
441
					thisHasNulls = thisHasNulls ||
442
						this.extra[4][i] != 0 ||
443
						this.extra[5][i] != 0;
444
				}
172
			}
445
			}
173
		return this;
174
	}
175
176
	/**
177
	 * Answers a copy of the current instance
178
	 */
179
	public FlowInfo copy() {
180
		
181
		// do not clone the DeadEnd
182
		if (this == DEAD_END)
183
			return this;
184
	
185
		// look for an unused preallocated object
186
		UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
187
	
188
		// copy slots
189
		copy.definiteInits = this.definiteInits;
190
		copy.potentialInits = this.potentialInits;
191
		copy.definiteNulls = this.definiteNulls;
192
		copy.definiteNonNulls = this.definiteNonNulls;
193
		copy.reachMode = this.reachMode;
194
		copy.maxFieldCount = this.maxFieldCount;
195
		
196
		if (this.extraDefiniteInits != null) {
197
			int length;
198
			System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[length = extraDefiniteInits.length]), 0, length);
199
			System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length);
200
			System.arraycopy(this.extraDefiniteNulls, 0, (copy.extraDefiniteNulls = new long[length]), 0, length);
201
			System.arraycopy(this.extraDefiniteNonNulls, 0, (copy.extraDefiniteNonNulls = new long[length]), 0, length);
202
		}
446
		}
203
		return copy;
204
	}
447
	}
205
	
448
	if (thisHasNulls) {
206
	public UnconditionalFlowInfo discardFieldInitializations(){
449
		this.tagBits |= NULL_FLAG_MASK; 
207
		
450
	}
208
		int limit = this.maxFieldCount;
451
	else {
209
		
452
		this.tagBits &= NULL_FLAG_MASK; 
210
		if (limit < BitCacheSize) {
211
			long mask = (1L << limit)-1;
212
			this.definiteInits &= ~mask;
213
			this.potentialInits &= ~mask;
214
			this.definiteNulls &= ~mask;
215
			this.definiteNonNulls &= ~mask;
216
			return this;
217
		} 
218
219
		this.definiteInits = 0;
220
		this.potentialInits = 0;
221
		this.definiteNulls = 0;
222
		this.definiteNonNulls = 0;
223
		
224
		// use extra vector
225
		if (extraDefiniteInits == null) {
226
			return this; // if vector not yet allocated, then not initialized
227
		}
228
		int vectorIndex, length = this.extraDefiniteInits.length;
229
		if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
230
			return this; // not enough room yet
231
		}
232
		for (int i = 0; i < vectorIndex; i++) {
233
			this.extraDefiniteInits[i] = 0L;
234
			this.extraPotentialInits[i] = 0L;
235
			this.extraDefiniteNulls[i] = 0L;
236
			this.extraDefiniteNonNulls[i] = 0L;
237
		}
238
		long mask = (1L << (limit % BitCacheSize))-1;
239
		this.extraDefiniteInits[vectorIndex] &= ~mask;
240
		this.extraPotentialInits[vectorIndex] &= ~mask;
241
		this.extraDefiniteNulls[vectorIndex] &= ~mask;
242
		this.extraDefiniteNonNulls[vectorIndex] &= ~mask;
243
		return this;
244
	}
453
	}
454
	return this;
455
}
245
456
246
	public UnconditionalFlowInfo discardNonFieldInitializations(){
457
public FlowInfo copy() {
247
		
458
	// do not clone the DeadEnd
248
		int limit = this.maxFieldCount;
459
	if (this == DEAD_END) {
249
		
250
		if (limit < BitCacheSize) {
251
			long mask = (1L << limit)-1;
252
			this.definiteInits &= mask;
253
			this.potentialInits &= mask;
254
			this.definiteNulls &= mask;
255
			this.definiteNonNulls &= mask;
256
			return this;
257
		} 
258
		// use extra vector
259
		if (extraDefiniteInits == null) {
260
			return this; // if vector not yet allocated, then not initialized
261
		}
262
		int vectorIndex, length = this.extraDefiniteInits.length;
263
		if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
264
			return this; // not enough room yet
265
		}
266
		long mask = (1L << (limit % BitCacheSize))-1;
267
		this.extraDefiniteInits[vectorIndex] &= mask;
268
		this.extraPotentialInits[vectorIndex] &= mask;
269
		this.extraDefiniteNulls[vectorIndex] &= mask;
270
		this.extraDefiniteNonNulls[vectorIndex] &= mask;
271
		for (int i = vectorIndex+1; i < length; i++) {
272
			this.extraDefiniteInits[i] = 0L;
273
			this.extraPotentialInits[i] = 0L;
274
			this.extraDefiniteNulls[i] = 0L;
275
			this.extraDefiniteNonNulls[i] = 0L;
276
		}
277
		return this;
460
		return this;
278
	}
461
	}
279
	
462
	UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
280
	public UnconditionalFlowInfo discardNullRelatedInitializations(){
463
	// copy slots
281
		
464
	copy.definiteInits = this.definiteInits;
282
		this.definiteNulls = 0;
465
	copy.potentialInits = this.potentialInits;
283
		this.definiteNonNulls = 0;
466
	boolean hasNullInfo = (this.tagBits & NULL_FLAG_MASK) != 0;
284
		
467
	if (hasNullInfo) { 
285
		int length = this.extraDefiniteInits == null ? 0 : this.extraDefiniteInits.length;
468
		copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1;
286
		for (int i = 0; i < length; i++) {
469
		copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2;
287
			this.extraDefiniteNulls[i] = 0L;
470
		copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1;
288
			this.extraDefiniteNonNulls[i] = 0L;
471
		copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2;
472
	}
473
	copy.tagBits = this.tagBits;
474
	copy.maxFieldCount = this.maxFieldCount;
475
	if (this.extra != null) {
476
		int length;
477
		copy.extra = new long[extraLength][];
478
		System.arraycopy(this.extra[0], 0, 
479
			(copy.extra[0] = new long[length = this.extra[0].length]), 0, 
480
			length);
481
		System.arraycopy(this.extra[1], 0, 
482
			(copy.extra[1] = new long[length]), 0, length);
483
		if (hasNullInfo) {
484
			for (int j = 2; j < extraLength; j++) {
485
				System.arraycopy(this.extra[j], 0, 
486
					(copy.extra[j] = new long[length]), 0, length);
487
			}
488
		}
489
		else {
490
			for (int j = 2; j < extraLength; j++) {
491
				copy.extra[j] = new long[length];
492
			}
289
		}
493
		}
290
		return this;
291
	}
494
	}
495
	return copy;
496
}
292
497
293
	public FlowInfo initsWhenFalse() {
498
/**
294
		
499
 * Remove local variables information from this flow info and return this.
295
		return this;
500
 * @return this, deprived from any local variable information
296
	}
501
 */
297
	
502
public UnconditionalFlowInfo discardNonFieldInitializations() {
298
	public FlowInfo initsWhenTrue() {
503
	int limit = this.maxFieldCount;
299
		
504
	if (limit < BitCacheSize) {
300
		return this;
505
		long mask = (1L << limit)-1;
506
		this.definiteInits &= mask;
507
		this.potentialInits &= mask;
508
		this.nullAssignmentStatusBit1 &= mask;
509
		this.nullAssignmentStatusBit2 &= mask;
510
		this.nullAssignmentValueBit1 &= mask;
511
		this.nullAssignmentValueBit2 &= mask;
512
	} 
513
	// use extra vector
514
	if (this.extra == null) {
515
		return this; // if vector not yet allocated, then not initialized
516
	}
517
	int vectorIndex, length = this.extra[0].length;
518
	if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
519
		return this; // not enough room yet
301
	}
520
	}
302
	
521
	if (vectorIndex >= 0) { 
303
	/**
522
		// else we only have complete non field array items left
304
	 * Check status of definite assignment at a given position.
523
		long mask = (1L << (limit % BitCacheSize))-1;
305
	 * It deals with the dual representation of the InitializationInfo2:
524
		for (int j = 0; j < extraLength; j++) {
306
	 * bits for the first 64 entries, then an array of booleans.
525
			this.extra[j][vectorIndex] &= mask;
307
	 */
308
	final private boolean isDefinitelyAssigned(int position) {
309
		
310
		// Dependant of CodeStream.isDefinitelyAssigned(..)
311
		// id is zero-based
312
		if (position < BitCacheSize) {
313
			return (definiteInits & (1L << position)) != 0; // use bits
314
		}
526
		}
315
		// use extra vector
316
		if (extraDefiniteInits == null)
317
			return false; // if vector not yet allocated, then not initialized
318
		int vectorIndex;
319
		if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length)
320
			return false; // if not enough room in vector, then not initialized 
321
		return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
322
	}
527
	}
323
	
528
	for (int i = vectorIndex + 1; i < length; i++) {
324
	/**
529
		for (int j = 0; j < extraLength; j++) {
325
	 * Check status of definite non-null assignment at a given position.
530
			this.extra[j][i] = 0;
326
	 * It deals with the dual representation of the InitializationInfo2:
327
	 * bits for the first 64 entries, then an array of booleans.
328
	 */
329
	final private boolean isDefinitelyNonNull(int position) {
330
		
331
		// Dependant of CodeStream.isDefinitelyAssigned(..)
332
		// id is zero-based
333
		if (position < BitCacheSize) {
334
			return (definiteNonNulls & (1L << position)) != 0; // use bits
335
		}
531
		}
336
		// use extra vector
337
		if (extraDefiniteNonNulls == null)
338
			return false; // if vector not yet allocated, then not initialized
339
		int vectorIndex;
340
		if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNonNulls.length)
341
			return false; // if not enough room in vector, then not initialized 
342
		return ((extraDefiniteNonNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
343
	}
344
345
	/**
346
	 * Check status of definite null assignment at a given position.
347
	 * It deals with the dual representation of the InitializationInfo2:
348
	 * bits for the first 64 entries, then an array of booleans.
349
	 */
350
	final private boolean isDefinitelyNull(int position) {
351
		
352
		// Dependant of CodeStream.isDefinitelyAssigned(..)
353
		// id is zero-based
354
		if (position < BitCacheSize) {
355
			return (definiteNulls & (1L << position)) != 0; // use bits
356
		}
357
		// use extra vector
358
		if (extraDefiniteNulls == null)
359
			return false; // if vector not yet allocated, then not initialized
360
		int vectorIndex;
361
		if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNulls.length)
362
			return false; // if not enough room in vector, then not initialized 
363
		return ((extraDefiniteNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
364
	}
365
366
	/**
367
	 * Check status of definite assignment for a field.
368
	 */
369
	final public boolean isDefinitelyAssigned(FieldBinding field) {
370
		
371
		// Dependant of CodeStream.isDefinitelyAssigned(..)
372
		// We do not want to complain in unreachable code
373
		if ((this.reachMode & UNREACHABLE) != 0)  
374
			return true;
375
		return isDefinitelyAssigned(field.id); 
376
	}
532
	}
377
	
533
	return this;
378
	/**
534
}
379
	 * Check status of definite assignment for a local.
535
380
	 */
536
public FlowInfo initsWhenFalse() {
381
	final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
537
	return this;
382
		
538
}
383
		// Dependant of CodeStream.isDefinitelyAssigned(..)
539
384
		// We do not want to complain in unreachable code
540
public FlowInfo initsWhenTrue() {
385
		if ((this.reachMode & UNREACHABLE) != 0)
541
	return this;
386
			return true;
542
}
387
543
388
		// final constants are inlined, and thus considered as always initialized
544
/**
389
		if (local.constant() != Constant.NotAConstant) {
545
 * Check status of definite assignment at a given position.
390
			return true;
546
 * It deals with the dual representation of the InitializationInfo2:
391
		}
547
 * bits for the first 64 entries, then an array of booleans.
392
		return isDefinitelyAssigned(local.id + maxFieldCount);
548
 */
549
final private boolean isDefinitelyAssigned(int position) {
550
	if (position < BitCacheSize) {
551
		// use bits
552
		return (this.definiteInits & (1L << position)) != 0; 
553
	}
554
	// use extra vector
555
	if (this.extra == null)
556
		return false; // if vector not yet allocated, then not initialized
557
	int vectorIndex;
558
	if ((vectorIndex = (position / BitCacheSize) - 1) 
559
			>= this.extra[0].length) {
560
		return false; // if not enough room in vector, then not initialized
393
	}
561
	}
394
	
562
	return ((this.extra[0][vectorIndex]) & 
395
	/**
563
				(1L << (position % BitCacheSize))) != 0;
396
	 * Check status of definite non-null assignment for a field.
564
}
397
	 */
565
398
	final public boolean isDefinitelyNonNull(FieldBinding field) {
566
final public boolean isDefinitelyAssigned(FieldBinding field) {
399
		
567
	// Dependant of CodeStream.isDefinitelyAssigned(..) 
400
		// Dependant of CodeStream.isDefinitelyAssigned(..)
568
	// do not want to complain in unreachable code
401
		// We do not want to complain in unreachable code
569
	if ((this.tagBits & UNREACHABLE) != 0) { 
402
		if ((this.reachMode & UNREACHABLE) != 0)  
570
		return true;
403
			return false;
404
		return isDefinitelyNonNull(field.id); 
405
	}
571
	}
406
	
572
	return isDefinitelyAssigned(field.id); 
407
	/**
573
}
408
	 * Check status of definite non-null assignment for a local.
574
409
	 */
575
final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
410
	final public boolean isDefinitelyNonNull(LocalVariableBinding local) {
576
	// do not want to complain in unreachable code
411
		
577
	if ((this.tagBits & UNREACHABLE) != 0) {
412
		// Dependant of CodeStream.isDefinitelyAssigned(..)
578
		return true;
413
		// We do not want to complain in unreachable code
579
	}
414
		if ((this.reachMode & UNREACHABLE) != 0)
580
	// final constants are inlined, and thus considered as always initialized
415
			return false;
581
	if (local.constant() != Constant.NotAConstant) {
416
		// final constants are inlined, and thus considered as always initialized
582
		return true;
417
		if (local.constant() != Constant.NotAConstant) {
418
			return true;
419
		}
420
		return isDefinitelyNonNull(local.id + maxFieldCount);
421
	}
422
423
	/**
424
	 * Check status of definite null assignment for a field.
425
	 */
426
	final public boolean isDefinitelyNull(FieldBinding field) {
427
		
428
		// Dependant of CodeStream.isDefinitelyAssigned(..)
429
		// We do not want to complain in unreachable code
430
		if ((this.reachMode & UNREACHABLE) != 0)  
431
			return false;
432
		return isDefinitelyNull(field.id); 
433
	}
583
	}
434
	
584
	return isDefinitelyAssigned(local.id + this.maxFieldCount);
435
	/**
585
}
436
	 * Check status of definite null assignment for a local.
586
437
	 */
587
final public boolean isDefinitelyNonNull(LocalVariableBinding local) {
438
	final public boolean isDefinitelyNull(LocalVariableBinding local) {
588
	// do not want to complain in unreachable code
439
		
589
	if ((this.tagBits & UNREACHABLE) != 0 || 
440
		// Dependant of CodeStream.isDefinitelyAssigned(..)
590
			(this.tagBits & NULL_FLAG_MASK) == 0) {
441
		// We do not want to complain in unreachable code
591
		return false;
442
		if ((this.reachMode & UNREACHABLE) != 0)
592
	}
443
			return false;
593
	if ((local.type.tagBits & TagBits.IsBaseType) != 0 || 
444
		return isDefinitelyNull(local.id + maxFieldCount);
594
			local.constant() != Constant.NotAConstant) { 
595
		return true;
596
	}
597
	int position = local.id + this.maxFieldCount;
598
	long mask;
599
	if (position < BitCacheSize) { // use bits
600
		return 
601
			(this.nullAssignmentStatusBit2 & 
602
				(mask = 1L << position)) != 0 ?
603
			(this.nullAssignmentStatusBit1 & mask) != 0 :
604
			(this.nullAssignmentStatusBit1 & 
605
				this.nullAssignmentValueBit2 & mask) != 0 &&
606
			(this.nullAssignmentValueBit1 & mask) == 0; 
607
	}
608
	// use extra vector
609
	if (this.extra == null) {
610
		return false; // if vector not yet allocated, then not initialized
611
	}
612
	int vectorIndex;
613
	if ((vectorIndex = (position / BitCacheSize) - 1)  
614
			>= this.extra[0].length) {
615
		return false; // if not enough room in vector, then not initialized
616
	}
617
	return 
618
		(this.extra[3][vectorIndex] & 
619
			(mask = 1L << (position % BitCacheSize))) != 0 ?
620
		(this.extra[2][vectorIndex] & mask) != 0 :
621
		(this.extra[2][vectorIndex] & 
622
			this.extra[5][vectorIndex] & mask) != 0 &&
623
		(this.extra[4][vectorIndex] & mask) == 0;
624
}
625
626
final public boolean isDefinitelyNull(LocalVariableBinding local) {
627
	// do not want to complain in unreachable code
628
	if ((this.tagBits & UNREACHABLE) != 0 || 
629
			(this.tagBits & NULL_FLAG_MASK) == 0 || 
630
			(local.type.tagBits & TagBits.IsBaseType) != 0) {
631
		return false;
632
	}
633
	int position = local.id + this.maxFieldCount;
634
	long mask;
635
	if (position < BitCacheSize) { // use bits
636
		return 
637
			(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
638
			(this.nullAssignmentStatusBit1 & mask) == 0 :
639
			(this.nullAssignmentStatusBit1 & 
640
				this.nullAssignmentValueBit1 & mask) != 0 &&
641
			(this.nullAssignmentValueBit2 & mask) == 0; 
642
	}
643
	// use extra vector
644
	if (this.extra == null) {
645
		return false; // if vector not yet allocated, then not initialized
646
	}
647
	int vectorIndex;
648
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
649
			this.extra[0].length) {
650
		return false; // if not enough room in vector, then not initialized
651
	}
652
	return
653
		(this.extra[3][vectorIndex] & 
654
			(mask = 1L << (position % BitCacheSize))) != 0 ?
655
		(this.extra[2][vectorIndex] & mask) == 0 :
656
		(this.extra[2][vectorIndex] & 
657
			this.extra[4][vectorIndex] & mask) != 0 &&
658
		(this.extra[5][vectorIndex] & mask) == 0;
659
}
660
661
final public boolean isDefinitelyUnknown(LocalVariableBinding local) {
662
	// do not want to complain in unreachable code
663
	if ((this.tagBits & UNREACHABLE) != 0 || 
664
			(this.tagBits & NULL_FLAG_MASK) == 0) {
665
		return false;
666
	}
667
	int position = local.id + this.maxFieldCount;
668
	long mask;
669
	if (position < BitCacheSize) { // use bits
670
		return 
671
			(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
672
			false :
673
			(this.nullAssignmentStatusBit1 & 
674
				this.nullAssignmentValueBit1 & 
675
				this.nullAssignmentValueBit2 & mask) != 0; 
676
	}
677
	// use extra vector
678
	if (this.extra == null) {
679
		return false; // if vector not yet allocated, then not initialized
680
	}
681
	int vectorIndex;
682
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
683
			this.extra[0].length) {
684
		return false; // if not enough room in vector, then not initialized
685
	}
686
	return
687
		(this.extra[3][vectorIndex] & 
688
			(mask = 1L << (position % BitCacheSize))) != 0 ?
689
		false :
690
		(this.extra[2][vectorIndex] & 
691
			this.extra[4][vectorIndex] &
692
			this.extra[5][vectorIndex] &
693
			mask) != 0;
694
}
695
696
/**
697
 * Check status of potential assignment at a given position.
698
 * It deals with the dual representation of the InitializationInfo3:
699
 * bits for the first 64 entries, then an array of booleans.
700
 */
701
final private boolean isPotentiallyAssigned(int position) {
702
	// id is zero-based
703
	if (position < BitCacheSize) {
704
		// use bits
705
		return (this.potentialInits & (1L << position)) != 0;
706
	}
707
	// use extra vector
708
	if (this.extra == null) {
709
		return false; // if vector not yet allocated, then not initialized
710
	}
711
	int vectorIndex;
712
	if ((vectorIndex = (position / BitCacheSize) - 1) 
713
			>= this.extra[0].length) {
714
		return false; // if not enough room in vector, then not initialized
445
	}
715
	}
716
	return ((this.extra[1][vectorIndex]) & 
717
			(1L << (position % BitCacheSize))) != 0;
718
}
719
720
/**
721
 * Check status of definite assignment for a field.
722
 */
723
final public boolean isPotentiallyAssigned(FieldBinding field) {
724
	return isPotentiallyAssigned(field.id); 
725
}
446
726
447
	public boolean isReachable() {
727
/**
448
		
728
 * Check status of potential assignment for a local.
449
		return this.reachMode == REACHABLE;
729
 */
730
final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
731
	// final constants are inlined, and thus considered as always initialized
732
	if (local.constant() != Constant.NotAConstant) {
733
		return true;
450
	}
734
	}
451
	
735
	return isPotentiallyAssigned(local.id + this.maxFieldCount);
452
	/**
736
}
453
	 * Check status of potential assignment at a given position.
737
454
	 * It deals with the dual representation of the InitializationInfo3:
738
final public boolean isPotentiallyNull(LocalVariableBinding local) {
455
	 * bits for the first 64 entries, then an array of booleans.
739
	if ((this.tagBits & NULL_FLAG_MASK) == 0 || 
456
	 */
740
			(local.type.tagBits & TagBits.IsBaseType) != 0) {
457
	final private boolean isPotentiallyAssigned(int position) {
741
		return false;
458
		
742
	}
459
		// id is zero-based
743
	int position;
460
		if (position < BitCacheSize) {
744
	long mask;
745
	if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
746
		// use bits
747
		return
748
			(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
749
			(this.nullAssignmentStatusBit1 & mask) == 0 : // protected null
750
			(this.nullAssignmentValueBit1 & mask) != 0 && // null bit set and
751
				((this.nullAssignmentStatusBit1 & mask) == 0 || // (potential or
752
				 (this.nullAssignmentValueBit2 & mask) == 0); 
753
											// assigned, but not unknown)
754
	}
755
	// use extra vector
756
	if (this.extra == null) {
757
		return false; // if vector not yet allocated, then not initialized
758
	}
759
	int vectorIndex;
760
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
761
			this.extra[0].length) {
762
		return false; // if not enough room in vector, then not initialized
763
	}
764
	return 
765
		(this.extra[3][vectorIndex] & 
766
			(mask = 1L << (position % BitCacheSize))) != 0 ?
767
		(this.extra[2][vectorIndex] & mask) == 0 :
768
		(this.extra[4][vectorIndex] & mask) != 0 && 
769
			((this.extra[2][vectorIndex] & mask) == 0 || 
770
			 (this.extra[5][vectorIndex] & mask) == 0); 
771
}
772
773
final public boolean isPotentiallyUnknown(LocalVariableBinding local) {
774
	// do not want to complain in unreachable code
775
	if ((this.tagBits & UNREACHABLE) != 0 || 
776
			(this.tagBits & NULL_FLAG_MASK) == 0) {
777
		return false;
778
	}
779
	int position = local.id + this.maxFieldCount;
780
	long mask;
781
	if (position < BitCacheSize) { // use bits
782
		return 
783
			(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
784
			false :
785
			((this.nullAssignmentStatusBit1 & 
786
				this.nullAssignmentValueBit1 |
787
			 ~this.nullAssignmentStatusBit1 &
788
				~this.nullAssignmentValueBit1) & 
789
				this.nullAssignmentValueBit2 & mask) != 0; 
790
	}
791
	// use extra vector
792
	if (this.extra == null) {
793
		return false; // if vector not yet allocated, then not initialized
794
	}
795
	int vectorIndex;
796
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
797
			this.extra[0].length) {
798
		return false; // if not enough room in vector, then not initialized
799
	}
800
	return
801
		(this.extra[3][vectorIndex] & 
802
			(mask = 1L << (position % BitCacheSize))) != 0 ?
803
		false :
804
		((this.extra[2][vectorIndex] & 
805
			this.extra[4][vectorIndex] |
806
		  ~this.extra[2][vectorIndex] &
807
			~this.extra[4][vectorIndex]) &
808
			this.extra[5][vectorIndex] &
809
			mask) != 0;
810
}
811
812
final public boolean isProtectedNonNull(LocalVariableBinding local) {
813
	if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
814
			(local.type.tagBits & TagBits.IsBaseType) != 0) {
815
		return false;
816
	}
817
	int position;
818
	if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
819
		// use bits
820
		return (this.nullAssignmentStatusBit1 &
821
				this.nullAssignmentStatusBit2 & (1L << position)) != 0;
822
	}
823
	// use extra vector
824
	if (this.extra == null) {
825
		return false; // if vector not yet allocated, then not initialized
826
	}
827
	int vectorIndex;
828
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
829
		this.extra[0].length) {
830
		return false; // if not enough room in vector, then not initialized
831
	}
832
	return (this.extra[4][vectorIndex] & 
833
			this.extra[5][vectorIndex] & 
834
			(1L << (position % BitCacheSize))) != 0;
835
}
836
837
final public boolean isProtectedNull(LocalVariableBinding local) {
838
	if ((this.tagBits & NULL_FLAG_MASK) == 0 || 
839
			(local.type.tagBits & TagBits.IsBaseType) != 0) {
840
		return false;
841
	}
842
	int position;
843
	if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
844
		// use bits
845
		return (~this.nullAssignmentStatusBit1 &
846
				this.nullAssignmentStatusBit2 & (1L << position)) != 0;
847
	}
848
	// use extra vector
849
	if (this.extra == null) {
850
		return false; // if vector not yet allocated, then not initialized
851
	}
852
	int vectorIndex;
853
	if ((vectorIndex = (position / BitCacheSize) - 1) >= 
854
			this.extra[0].length) {
855
		return false; // if not enough room in vector, then not initialized
856
	}
857
	return (~this.extra[4][vectorIndex] & 
858
			this.extra[5][vectorIndex] &
859
			(1L << (position % BitCacheSize))) != 0;
860
}
861
862
public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
863
	// protected from non-object locals in calling methods
864
	if (this != DEAD_END) {
865
		this.tagBits |= NULL_FLAG_MASK;
866
		int position;
867
		long mask;
868
		// position is zero-based
869
		if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
461
			// use bits
870
			// use bits
462
			return (potentialInits & (1L << position)) != 0;
871
			if (((mask = 1L << position) & // leave assigned non null unchanged 
872
					this.nullAssignmentStatusBit1 &
873
					~this.nullAssignmentStatusBit2 &
874
					~this.nullAssignmentValueBit1 &
875
					this.nullAssignmentValueBit2) == 0) {
876
				// set protected non null
877
				this.nullAssignmentStatusBit1 |= mask;
878
				this.nullAssignmentStatusBit2 |= mask;
879
				 // clear potential null
880
				this.nullAssignmentValueBit1 &= ~mask;
881
			}
882
		} 
883
		else {
884
			// use extra vector
885
			int vectorIndex = (position / BitCacheSize) - 1;
886
			if (this.extra == null) {
887
				int length = vectorIndex + 1;
888
				this.extra = new long[extraLength][];
889
				for (int j = 0; j < extraLength; j++) {
890
					this.extra[j] = new long[length];
891
				}
892
			}
893
			else {
894
				int oldLength;
895
				if (vectorIndex >= (oldLength = this.extra[0].length)) {
896
					int newLength = vectorIndex + 1;
897
					for (int j = 0; j < extraLength; j++) {
898
						System.arraycopy(this.extra[j], 0, 
899
							(this.extra[j] = new long[newLength]), 0, 
900
							oldLength);
901
					}
902
				}
903
			}
904
			if (((mask = 1L << (position % BitCacheSize)) & 
905
					this.extra[2][vectorIndex] &
906
					~this.extra[3][vectorIndex] &
907
					~this.extra[4][vectorIndex] &
908
					this.extra[5][vectorIndex]) == 0) {
909
				this.extra[2][vectorIndex] |= mask;
910
				this.extra[3][vectorIndex] |= mask;
911
				this.extra[4][vectorIndex] &= ~mask;
912
			}
463
		}
913
		}
464
		// use extra vector
465
		if (extraPotentialInits == null)
466
			return false; // if vector not yet allocated, then not initialized
467
		int vectorIndex;
468
		if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length)
469
			return false; // if not enough room in vector, then not initialized 
470
		return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
471
	}
472
	
473
	/**
474
	 * Check status of definite assignment for a field.
475
	 */
476
	final public boolean isPotentiallyAssigned(FieldBinding field) {
477
		
478
		return isPotentiallyAssigned(field.id); 
479
	}
914
	}
480
	
915
}
481
	/**
916
482
	 * Check status of potential assignment for a local.
917
public void markAsComparedEqualToNull(LocalVariableBinding local) {
483
	 */
918
	// protected from non-object locals in calling methods
484
	final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
919
	if (this != DEAD_END) {
485
		
920
		this.tagBits |= NULL_FLAG_MASK;
486
		// final constants are inlined, and thus considered as always initialized
921
		int position;
487
		if (local.constant() != Constant.NotAConstant) {
922
		long mask, unknownAssigned;
488
			return true;
923
		// position is zero-based
924
		if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
925
			// use bits
926
			mask = 1L << position;
927
			if ((mask & // leave assigned null unchanged
928
					this.nullAssignmentStatusBit1 &
929
					~this.nullAssignmentStatusBit2 &
930
					this.nullAssignmentValueBit1 &
931
					~this.nullAssignmentValueBit2) == 0) {
932
				unknownAssigned = this.nullAssignmentStatusBit1 &
933
					~this.nullAssignmentStatusBit2 &
934
					this.nullAssignmentValueBit1 &
935
					this.nullAssignmentValueBit2;
936
				// set protected
937
				this.nullAssignmentStatusBit2 |= mask;
938
				this.nullAssignmentStatusBit1 &= (mask = ~mask);
939
				// protected is null
940
				this.nullAssignmentValueBit1 &= mask | ~unknownAssigned;
941
				this.nullAssignmentValueBit2 &= mask;
942
				// clear potential anything but null
943
			}
944
		} 
945
		else {
946
			// use extra vector
947
			int vectorIndex = (position / BitCacheSize) - 1;
948
			mask = 1L << (position % BitCacheSize);
949
			if (this.extra == null) {
950
				int length = vectorIndex + 1;
951
				this.extra = new long[extraLength][];
952
				for (int j = 0; j < extraLength; j++) {
953
					this.extra[j] = new long[length ];
954
				}
955
			}
956
			else {
957
				int oldLength;
958
				if (vectorIndex >= (oldLength = this.extra[0].length)) {
959
					int newLength = vectorIndex + 1;
960
					for (int j = 0; j < extraLength; j++) {
961
						System.arraycopy(this.extra[j], 0, 
962
							(this.extra[j] = new long[newLength]), 0,
963
							oldLength);
964
					}
965
				}
966
			}
967
			if ((mask &
968
					this.extra[2][vectorIndex] &
969
					~this.extra[3][vectorIndex] &
970
					this.extra[4][vectorIndex] &
971
					~this.extra[5][vectorIndex]) == 0) {
972
				unknownAssigned = this.extra[2][vectorIndex] &
973
					~this.extra[3][vectorIndex] &
974
					this.extra[4][vectorIndex] &
975
					this.extra[5][vectorIndex];
976
				this.extra[3][vectorIndex]	 |= mask;
977
				this.extra[2][vectorIndex] &= (mask = ~mask);
978
				this.extra[4][vectorIndex] &= mask | ~unknownAssigned;
979
				this.extra[5][vectorIndex]	&= mask;
980
			}
489
		}
981
		}
490
		return isPotentiallyAssigned(local.id + maxFieldCount);
491
	}
982
	}
983
}
984
985
/**
986
 * Record a definite assignment at a given position.
987
 * It deals with the dual representation of the InitializationInfo2:
988
 * bits for the first 64 entries, then an array of booleans.
989
 */
990
final private void markAsDefinitelyAssigned(int position) {
492
	
991
	
493
	/**
992
	if (this != DEAD_END) {
494
	 * Record a definite assignment at a given position.
993
		// position is zero-based
495
	 * It deals with the dual representation of the InitializationInfo2:
994
		if (position < BitCacheSize) {
496
	 * bits for the first 64 entries, then an array of booleans.
995
			// use bits
497
	 */
996
			long mask;
498
	final private void markAsDefinitelyAssigned(int position) {
997
			this.definiteInits |= (mask = 1L << position);
499
		
998
			this.potentialInits |= mask;
500
		if (this != DEAD_END) {
999
		} 
501
	
1000
		else {
502
			// position is zero-based
1001
			// use extra vector
503
			if (position < BitCacheSize) {
1002
			int vectorIndex = (position / BitCacheSize) - 1;
504
				// use bits
1003
			if (this.extra == null) {
505
				long mask;
1004
				int length = vectorIndex + 1;
506
				definiteInits |= (mask = 1L << position);
1005
				this.extra = new long[extraLength][];
507
				potentialInits |= mask;
1006
				for (int j = 0; j < extraLength; j++) {
508
				definiteNulls &= ~mask;
1007
					this.extra[j] = new long[length];
509
				definiteNonNulls &= ~mask;
1008
				}
510
			} else {
1009
			} 
511
				// use extra vector
1010
			else {
512
				int vectorIndex = (position / BitCacheSize) - 1;
1011
				int oldLength; // might need to grow the arrays
513
				if (extraDefiniteInits == null) {
1012
				if (vectorIndex >= (oldLength = this.extra[0].length)) {
514
					int length;
1013
					for (int j = 0; j < extraLength; j++) {
515
					extraDefiniteInits = new long[length = vectorIndex + 1];
1014
						System.arraycopy(this.extra[j], 0, 
516
					extraPotentialInits = new long[length];
1015
							(this.extra[j] = new long[vectorIndex + 1]), 0, 
517
					extraDefiniteNulls = new long[length];
1016
							oldLength);
518
					extraDefiniteNonNulls = new long[length];
519
				} else {
520
					int oldLength; // might need to grow the arrays
521
					if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
522
						System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength);
523
						System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength);
524
						System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[vectorIndex + 1]), 0, oldLength);
525
						System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[vectorIndex + 1]), 0, oldLength);
526
					}
1017
					}
527
				}
1018
				}
528
				long mask;
529
				extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
530
				extraPotentialInits[vectorIndex] |= mask;
531
				extraDefiniteNulls[vectorIndex] &= ~mask;
532
				extraDefiniteNonNulls[vectorIndex] &= ~mask;
533
			}
1019
			}
1020
			long mask;
1021
			this.extra[0][vectorIndex] |= 
1022
				(mask = 1L << (position % BitCacheSize));
1023
			this.extra[1][vectorIndex] |= mask;
534
		}
1024
		}
535
	}
1025
	}
536
	
1026
}
537
	/**
1027
538
	 * Record a field got definitely assigned.
1028
public void markAsDefinitelyAssigned(FieldBinding field) {
539
	 */
1029
	if (this != DEAD_END)
540
	public void markAsDefinitelyAssigned(FieldBinding field) {
1030
		markAsDefinitelyAssigned(field.id);
541
		if (this != DEAD_END)
1031
}
542
			markAsDefinitelyAssigned(field.id);
1032
1033
public void markAsDefinitelyAssigned(LocalVariableBinding local) {
1034
	if (this != DEAD_END)
1035
		markAsDefinitelyAssigned(local.id + this.maxFieldCount);
1036
}
1037
1038
/**
1039
 * Record a definite non-null assignment at a given position.
1040
 */
1041
final private void markAsDefinitelyNonNull(int position) {
1042
	// DEAD_END guarded above
1043
	this.tagBits |= NULL_FLAG_MASK;
1044
	long mask;
1045
	// position is zero-based
1046
	if (position < BitCacheSize) {
1047
		// use bits
1048
		this.nullAssignmentStatusBit1 |= (mask = 1L << position);
1049
		this.nullAssignmentValueBit2 |= mask; // set non null
1050
		this.nullAssignmentStatusBit2 &= ~mask; // clear protection
1051
		this.nullAssignmentValueBit1 &= ~mask; // clear null
1052
	} 
1053
	else {
1054
		// use extra vector
1055
		int vectorIndex = (position / BitCacheSize) - 1;
1056
		this.extra[2][vectorIndex] |= 
1057
			(mask = 1L << (position % BitCacheSize));
1058
		this.extra[5][vectorIndex] |= mask;
1059
		this.extra[3][vectorIndex] &= ~mask;
1060
		this.extra[4][vectorIndex] &= ~mask;
543
	}
1061
	}
544
	
1062
}
545
	/**
1063
546
	 * Record a local got definitely assigned.
1064
public void markAsDefinitelyNonNull(FieldBinding field) {
547
	 */
1065
	if (this != DEAD_END) {
548
	public void markAsDefinitelyAssigned(LocalVariableBinding local) {
1066
		markAsDefinitelyNonNull(field.id);
549
		if (this != DEAD_END)
550
			markAsDefinitelyAssigned(local.id + maxFieldCount);
551
	}
552
553
	/**
554
	 * Record a definite non-null assignment at a given position.
555
	 * It deals with the dual representation of the InitializationInfo2:
556
	 * bits for the first 64 entries, then an array of booleans.
557
	 */
558
	final private void markAsDefinitelyNonNull(int position) {
559
		
560
		if (this != DEAD_END) {
561
	
562
			// position is zero-based
563
			if (position < BitCacheSize) {
564
				// use bits
565
				long mask;
566
				definiteNonNulls |= (mask = 1L << position);
567
				definiteNulls &= ~mask;
568
			} else {
569
				// use extra vector
570
				int vectorIndex = (position / BitCacheSize) - 1;
571
				long mask;
572
				extraDefiniteNonNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
573
				extraDefiniteNulls[vectorIndex] &= ~mask;
574
			}
575
		}
576
	}
1067
	}
1068
}
577
1069
578
	/**
1070
public void markAsDefinitelyNonNull(LocalVariableBinding local) {
579
	 * Record a field got definitely assigned to non-null value.
1071
	// protected from non-object locals in calling methods
580
	 */
1072
	if (this != DEAD_END) {
581
	public void markAsDefinitelyNonNull(FieldBinding field) {
1073
		markAsDefinitelyNonNull(local.id + this.maxFieldCount);
582
		if (this != DEAD_END)
583
			markAsDefinitelyNonNull(field.id);
584
	}
1074
	}
585
	
1075
}
586
	/**
1076
587
	 * Record a local got definitely assigned to non-null value.
1077
/**
588
	 */
1078
 * Record a definite null assignment at a given position.
589
	public void markAsDefinitelyNonNull(LocalVariableBinding local) {
1079
 */
590
		if (this != DEAD_END)
1080
final private void markAsDefinitelyNull(int position) {
591
			markAsDefinitelyNonNull(local.id + maxFieldCount);
1081
	// DEAD_END guarded above
592
	}
1082
	this.tagBits |= NULL_FLAG_MASK;
593
1083
	long mask;
594
	/**
1084
	if (position < BitCacheSize) {
595
	 * Record a definite null assignment at a given position.
1085
		// use bits
596
	 * It deals with the dual representation of the InitializationInfo2:
1086
		this.nullAssignmentStatusBit1 |= (mask = 1L << position); // set assignment
597
	 * bits for the first 64 entries, then an array of booleans.
1087
		this.nullAssignmentStatusBit2 &= ~mask; // clear protection
598
	 */
1088
		this.nullAssignmentValueBit1 |= mask; // set null
599
	final private void markAsDefinitelyNull(int position) {
1089
		this.nullAssignmentValueBit2 &= ~mask; // clear non null
600
		
1090
	} 
601
		if (this != DEAD_END) {
1091
	else {
602
	
1092
		// use extra vector
603
			// position is zero-based
1093
		int vectorIndex = (position / BitCacheSize) - 1;
604
			if (position < BitCacheSize) {
1094
		this.extra[2][vectorIndex] |= 
605
				// use bits
1095
			(mask = 1L << (position % BitCacheSize));
606
				long mask;
1096
		this.extra[3][vectorIndex] &= ~mask;
607
				definiteNulls |= (mask = 1L << position);
1097
		this.extra[4][vectorIndex] |= mask;
608
				definiteNonNulls &= ~mask;
1098
		this.extra[5][vectorIndex] &= ~mask;
609
			} else {
610
				// use extra vector
611
				int vectorIndex = (position / BitCacheSize) - 1;
612
				long mask;
613
				extraDefiniteNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
614
				extraDefiniteNonNulls[vectorIndex] &= ~mask;
615
			}
616
		}
617
	}
1099
	}
1100
}
618
1101
619
	/**
1102
public void markAsDefinitelyNull(FieldBinding field) {
620
	 * Record a field got definitely assigned to null.
1103
	if (this != DEAD_END) {
621
	 */
1104
		markAsDefinitelyNull(field.id);
622
	public void markAsDefinitelyNull(FieldBinding field) {
623
		if (this != DEAD_END)
624
			markAsDefinitelyAssigned(field.id);
625
	}
1105
	}
626
	
1106
}
627
	/**
1107
628
	 * Record a local got definitely assigned to null.
1108
public void markAsDefinitelyNull(LocalVariableBinding local) {
629
	 */
1109
	// protected from non-object locals in calling methods
630
	public void markAsDefinitelyNull(LocalVariableBinding local) {
1110
	if (this != DEAD_END) {
631
		if (this != DEAD_END)
1111
		markAsDefinitelyNull(local.id + this.maxFieldCount);
632
			markAsDefinitelyNull(local.id + maxFieldCount);
633
	}
1112
	}
634
	
1113
}
635
	/**
1114
636
	 * Clear initialization information at a given position.
1115
/**
637
	 * It deals with the dual representation of the InitializationInfo2:
1116
 * Mark a local as having been assigned to an unknown value.
638
	 * bits for the first 64 entries, then an array of booleans.
1117
 * @param local the local to mark
639
	 */
1118
 */
640
	final private void markAsDefinitelyNotAssigned(int position) {
1119
// PREMATURE may try to get closer to markAsDefinitelyAssigned, but not
641
		if (this != DEAD_END) {
1120
//			 obvious
642
	
1121
public void markAsDefinitelyUnknown(LocalVariableBinding local) {
643
			// position is zero-based
1122
	// protected from non-object locals in calling methods
644
			if (position < BitCacheSize) {
1123
	if (this != DEAD_END) {
645
				// use bits
1124
		this.tagBits |= NULL_FLAG_MASK;
646
				long mask;
1125
		long mask;
647
				definiteInits &= ~(mask = 1L << position);
1126
		int position;
648
				potentialInits &= ~mask;
1127
		// position is zero-based
649
				definiteNulls &= ~mask;
1128
		if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
650
				definiteNonNulls &= ~mask;
1129
			// use bits
651
			} else {
1130
			this.nullAssignmentValueBit1 |= (mask = 1L << position);
652
				// use extra vector
1131
			this.nullAssignmentValueBit2 |= mask;
653
				int vectorIndex = (position / BitCacheSize) - 1;
1132
			// set unknown
654
				if (extraDefiniteInits == null) {
1133
			this.nullAssignmentStatusBit1 |= mask;
655
					return; // nothing to do, it was not yet set 
1134
			// set assignment
656
				}
1135
			this.nullAssignmentStatusBit2 &= ~mask;
657
				// might need to grow the arrays
1136
			// clear protection
658
				if (vectorIndex >= extraDefiniteInits.length) {
1137
		} 
659
					return; // nothing to do, it was not yet set 
1138
		else {
660
				}
1139
			// use extra vector
661
				long mask;
1140
			int vectorIndex = (position / BitCacheSize) - 1;
662
				extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
1141
			this.extra[4][vectorIndex] |=
663
				extraPotentialInits[vectorIndex] &= ~mask;
1142
				(mask = 1L << (position % BitCacheSize));
664
				extraDefiniteNulls[vectorIndex] &= ~mask;
1143
			this.extra[5][vectorIndex] |= mask;
665
				extraDefiniteNonNulls[vectorIndex] &= ~mask;
1144
			this.extra[2][vectorIndex] |= mask;
666
			}
1145
			this.extra[3][vectorIndex] &= ~mask;
667
		}
1146
		}
668
	}
1147
	}
669
	
1148
}
670
	/**
1149
671
	 * Clear the initialization info for a field
1150
public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
672
	 */
1151
	if ((otherInits.tagBits & UNREACHABLE) != 0 && this != DEAD_END) {
673
	public void markAsDefinitelyNotAssigned(FieldBinding field) {
1152
		// DEAD_END + unreachable other -> other
674
		
1153
		return this;
675
		if (this != DEAD_END)
676
			markAsDefinitelyNotAssigned(field.id);
677
	}
1154
	}
678
	
1155
	if ((this.tagBits & UNREACHABLE) != 0) {
679
	/**
1156
		return (UnconditionalFlowInfo) otherInits.copy(); // make sure otherInits won't be affected
680
	 * Clear the initialization info for a local variable
1157
	} 
681
	 */
1158
	
682
	
1159
	// intersection of definitely assigned variables, 
683
	public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
1160
	this.definiteInits &= otherInits.definiteInits;
684
		
1161
	// union of potentially set ones
685
		if (this != DEAD_END)
1162
	this.potentialInits |= otherInits.potentialInits;
686
			markAsDefinitelyNotAssigned(local.id + maxFieldCount);
1163
687
	}
1164
	// null combinations
688
		
1165
	boolean otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0,
689
	/**
1166
		thisHasNulls = false;
690
	 * Returns the receiver updated in the following way: <ul>
1167
	long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4;
691
	 * <li> intersection of definitely assigned variables, 
1168
	if (otherHasNulls) {
692
	 * <li> union of potentially assigned variables.
1169
		this.nullAssignmentStatusBit1 =
693
	 * </ul>
1170
			(a1 = this.nullAssignmentStatusBit1) & 
694
	 */
1171
			(b1 = otherInits.nullAssignmentStatusBit1) & (
695
	public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
1172
				(nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) & 
696
	
1173
				((b2 = otherInits.nullAssignmentStatusBit2) & 
697
		if (this == DEAD_END) return otherInits;
1174
						(nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) & 
698
		if (otherInits == DEAD_END) return this;
1175
						(na3 = ~(a3 = this.nullAssignmentValueBit1)) & 
699
	
1176
						((a2 = this.nullAssignmentStatusBit2) & 
700
		if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){
1177
							(na4 = ~(a4 = this.nullAssignmentValueBit2)) | a4) |
701
			if ((this.reachMode & UNREACHABLE) != 0){
1178
						(na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) |
702
				return otherInits;
1179
				b4 & (na3 & nb3 & (na4 & a2 | a4) |
703
			} 
1180
						na2 & a4 & nb2));
704
			return this;
1181
		this.nullAssignmentStatusBit2 =
705
		}
1182
			a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) |
706
		
1183
			a1 & b1 & (a2 ^ b2) & na3 & nb3 |
707
		// if one branch is not fake reachable, then the merged one is reachable
1184
			(a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4;
708
		this.reachMode &= otherInits.reachMode;
1185
		this.nullAssignmentValueBit1 =
709
	
1186
			b1 & nb2 & nb4 |
710
		// intersection of definitely assigned variables, 
1187
			~a1 & (a3 |
711
		this.definiteInits &= otherInits.definiteInits;
1188
					a2 & na3 & (b1 | nb2)) |
712
		// union of potentially set ones
1189
			(a1 | na2) & nb1 & b2 & nb3 |
713
		this.potentialInits |= otherInits.potentialInits;
1190
			nb1 & b3 |
714
		// intersection of definitely null variables, 
1191
			a1 & na2 & (na4 |
715
		this.definiteNulls &= otherInits.definiteNulls;
1192
						b1 & nb2 & (a3 | b3));
716
		// intersection of definitely non-null variables, 
1193
		this.nullAssignmentValueBit2 =
717
		this.definiteNonNulls &= otherInits.definiteNonNulls;
1194
			a4 | b4;
718
	
1195
	}
719
		// treating extra storage
1196
	else {
720
		if (this.extraDefiniteInits != null) {
1197
		// tune potentials
721
			if (otherInits.extraDefiniteInits != null) {
1198
		this.nullAssignmentValueBit1 =
1199
			~(~this.nullAssignmentStatusBit1 &
1200
					~this.nullAssignmentStatusBit2 &
1201
					~this.nullAssignmentValueBit1) &
1202
			~(this.nullAssignmentStatusBit1 & 
1203
					(this.nullAssignmentStatusBit2 | this.nullAssignmentValueBit2));
1204
		// reset assignment and protected
1205
		this.nullAssignmentStatusBit1 = 
1206
		this.nullAssignmentStatusBit2 = 0;
1207
	}
1208
	thisHasNulls = this.nullAssignmentStatusBit1 != 0 || 
1209
		this.nullAssignmentStatusBit2 != 0 ||
1210
		this.nullAssignmentValueBit1 != 0 ||
1211
		this.nullAssignmentValueBit2 != 0;
1212
1213
	// treating extra storage
1214
	if (this.extra != null || otherInits.extra != null) {
1215
		int mergeLimit = 0, copyLimit = 0, resetLimit = 0;
1216
		if (this.extra != null) {
1217
			if (otherInits.extra != null) {
722
				// both sides have extra storage
1218
				// both sides have extra storage
723
				int i = 0, length, otherLength;
1219
				int length, otherLength;
724
				if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
1220
				if ((length = this.extra[0].length) < 
725
					// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
1221
						(otherLength = otherInits.extra[0].length)) {
726
					System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
1222
					// current storage is shorter -> grow current 
727
					System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
1223
					for (int j = 0; j < extraLength; j++) {
728
					System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length);
1224
						System.arraycopy(this.extra[j], 0, 
729
					System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length);
1225
							(this.extra[j] = new long[otherLength]), 0, length);
730
					while (i < length) {
731
						this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
732
						this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
733
						this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
734
						this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
735
					}
1226
					}
736
					while (i < otherLength) {
1227
					mergeLimit = length;
737
						this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
1228
					copyLimit = otherLength;
738
					}
1229
				} 
739
				} else {
1230
				else {
740
					// current storage is longer
1231
					// current storage is longer
741
					while (i < otherLength) {
1232
					mergeLimit = otherLength;
742
						this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
1233
					resetLimit = length;
743
						this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
744
						this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
745
						this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
746
					}
747
					while (i < length) {
748
						this.extraDefiniteInits[i] = 0;
749
						this.extraDefiniteNulls[i] = 0;
750
						this.extraDefiniteNonNulls[i++] = 0;
751
					}
752
				}
1234
				}
753
			} else {
1235
			} 
754
				// no extra storage on otherInits
1236
			else {
755
				int i = 0, length = this.extraDefiniteInits.length;
1237
				resetLimit = this.extra[0].length;
756
				while (i < length) {
757
					this.extraDefiniteInits[i] = 0;
758
					this.extraDefiniteNulls[i] = 0;
759
					this.extraDefiniteNonNulls[i++] = 0;
760
				}
761
			}
762
		} else
763
			if (otherInits.extraDefiniteInits != null) {
764
				// no storage here, but other has extra storage.
765
				int otherLength;
766
				this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
767
				System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
768
				this.extraDefiniteNulls = new long[otherLength];
769
				this.extraDefiniteNonNulls = new long[otherLength];
770
			}
1238
			}
771
		return this;
1239
		} 
1240
		else if (otherInits.extra != null) {
1241
			// no storage here, but other has extra storage.
1242
			int otherLength = otherInits.extra[0].length;
1243
			this.extra = new long[extraLength][];
1244
			for (int j = 0; j < extraLength; j++) {
1245
				this.extra[j] = new long[otherLength];
1246
			}
1247
			System.arraycopy(otherInits.extra[1], 0, 
1248
				this.extra[1], 0, otherLength);
1249
			copyLimit = otherLength;
1250
		}
1251
		int i;
1252
		if (otherHasNulls) {
1253
			for (i = 0; i < mergeLimit; i++) {
1254
				this.extra[2][i] =
1255
					(a1 = this.extra[2][i]) & 
1256
					(b1 = otherInits.extra[2][i]) & (
1257
						(nb4 = ~(b4 = otherInits.extra[5][i])) & 
1258
						((b2 = otherInits.extra[3][i]) & 
1259
								(nb3 = ~(b3 = otherInits.extra[4][i])) & 
1260
								(na3 = ~(a3 = this.extra[4][i])) & 
1261
								((a2 = this.extra[3][i]) & 
1262
									(na4 = ~(a4 = this.extra[5][i])) | a4) |
1263
								(na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) |
1264
						b4 & (na3 & nb3 & (na4 & a2 | a4) |
1265
								na2 & a4 & nb2));
1266
				this.extra[3][i] =
1267
					a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) |
1268
					a1 & b1 & (a2 ^ b2) & na3 & nb3 |
1269
					(a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4;
1270
				this.extra[4][i] =
1271
					b1 & nb2 & nb4 |
1272
					~a1 & (a3 |
1273
							a2 & na3 & (b1 | nb2)) |
1274
					(a1 | na2) & nb1 & b2 & nb3 |
1275
					nb1 & b3 |
1276
					a1 & na2 & (na4 |
1277
								b1 & nb2 & (a3 | b3));
1278
				this.extra[5][i] =
1279
					a4 | b4;
1280
				thisHasNulls = thisHasNulls ||
1281
					this.extra[5][i] != 0 ||
1282
					this.extra[2][i] != 0 ||
1283
					this.extra[3][i] != 0 ||
1284
					this.extra[4][i] != 0;
1285
			}
1286
		}
1287
		else {
1288
			for (i = 0; i < mergeLimit; i++) {
1289
				this.extra[0][i] &= 
1290
					otherInits.extra[0][i];
1291
				this.extra[1][i] |= 
1292
					otherInits.extra[1][i];
1293
				this.extra[4][i] =
1294
					~(~this.extra[2][i] &
1295
							~this.extra[3][i] &
1296
							~this.extra[4][i]) &
1297
					~(this.extra[2][i] & 
1298
							(this.extra[3][i] | 
1299
							this.extra[5][i]));
1300
				this.extra[2][i] = 
1301
				this.extra[3][i] = 0;
1302
				thisHasNulls = thisHasNulls ||
1303
					this.extra[4][i] != 0 ||
1304
					this.extra[5][i] != 0;
1305
			}
1306
		}
1307
		for (; i < copyLimit; i++) {
1308
			this.extra[1][i] = otherInits.extra[1][i];
1309
			this.extra[4][i] =
1310
				~(~otherInits.extra[2][i] &
1311
					~otherInits.extra[3][i] &
1312
					~otherInits.extra[4][i]) &
1313
				~(otherInits.extra[2][i] & 
1314
					(otherInits.extra[3][i] |
1315
					otherInits.extra[5][i]));
1316
			this.extra[5][i] = otherInits.extra[5][i];
1317
			thisHasNulls = thisHasNulls ||
1318
				this.extra[4][i] != 0 ||
1319
				this.extra[5][i] != 0;
1320
		}
1321
		for (; i < resetLimit; i++) {
1322
			this.extra[4][i] =
1323
				~(~this.extra[2][i] &
1324
						~this.extra[3][i] &
1325
						~this.extra[4][i]) &
1326
				~(this.extra[2][i] & 
1327
						(this.extra[3][i] | 
1328
						this.extra[5][i]));
1329
			this.extra[0][i] = 
1330
			this.extra[2][i] = 
1331
			this.extra[3][i] = 0;
1332
			thisHasNulls = thisHasNulls ||
1333
				this.extra[4][i] != 0 ||
1334
				this.extra[5][i] != 0;
1335
		}
772
	}
1336
	}
773
	
1337
	if (thisHasNulls) {
774
	/*
1338
		this.tagBits |= NULL_FLAG_MASK;
775
	 * Answer the total number of fields in enclosing types of a given type
1339
	}
776
	 */
1340
	else {
777
	static int numberOfEnclosingFields(ReferenceBinding type){
1341
		this.tagBits &= ~NULL_FLAG_MASK;
778
		
1342
	}
779
		int count = 0;
1343
	return this;
1344
}
1345
1346
/*
1347
 * Answer the total number of fields in enclosing types of a given type
1348
 */
1349
static int numberOfEnclosingFields(ReferenceBinding type){
1350
	int count = 0;
1351
	type = type.enclosingType();
1352
	while(type != null) {
1353
		count += type.fieldCount();
780
		type = type.enclosingType();
1354
		type = type.enclosingType();
781
		while(type != null) {
782
			count += type.fieldCount();
783
			type = type.enclosingType();
784
		}
785
		return count;
786
	}
1355
	}
787
	
1356
	return count;
788
	public int reachMode(){
1357
}
789
		return this.reachMode;
1358
1359
public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() {
1360
	if (this == DEAD_END) {
1361
		return this;
790
	}
1362
	}
791
	
1363
	UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
792
	public FlowInfo setReachMode(int reachMode) {
1364
	copy.definiteInits = this.definiteInits;
793
		
1365
	copy.potentialInits = this.potentialInits;
794
		if (this == DEAD_END) return this; // cannot modify DEAD_END
1366
	copy.tagBits = this.tagBits & ~NULL_FLAG_MASK;
795
	
1367
	copy.maxFieldCount = this.maxFieldCount;
796
		// reset optional inits when becoming unreachable
1368
	if (this.extra != null) {
797
		if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) {
1369
		int length;
1370
		copy.extra = new long[extraLength][];
1371
		System.arraycopy(this.extra[0], 0, 
1372
			(copy.extra[0] = 
1373
				new long[length = this.extra[0].length]), 0, length);
1374
		System.arraycopy(this.extra[1], 0, 
1375
			(copy.extra[1] = new long[length]), 0, length);
1376
		for (int j = 2; j < extraLength; j++) {
1377
			copy.extra[j] = new long[length];
1378
		}
1379
	}
1380
	return copy;
1381
}
1382
1383
public FlowInfo safeInitsWhenTrue() {
1384
	return copy();
1385
}
1386
1387
public FlowInfo setReachMode(int reachMode) {
1388
	if (reachMode == REACHABLE && this != DEAD_END) { // cannot modify DEAD_END
1389
		this.tagBits &= ~UNREACHABLE;
1390
	}
1391
	else {
1392
		if ((this.tagBits & UNREACHABLE) == 0) {
1393
			// reset optional inits when becoming unreachable
1394
			// see InitializationTest#test090 (and others)
798
			this.potentialInits = 0;
1395
			this.potentialInits = 0;
799
			if (this.extraPotentialInits != null){
1396
			if (this.extra != null) {
800
				for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){
1397
				for (int i = 0, length = this.extra[0].length; 
801
					this.extraPotentialInits[i] = 0;
1398
						i < length; i++) {
1399
					this.extra[1][i] = 0;
802
				}
1400
				}
803
			}
1401
			}
804
		}				
1402
		}				
805
		this.reachMode = reachMode;
1403
		this.tagBits |= UNREACHABLE;
806
	
807
		return this;
808
	}
1404
	}
1405
	return this;
1406
}
809
1407
810
	public String toString(){
1408
public String toString(){
811
		
1409
	// PREMATURE consider printing bit fields as 0001 0001 1000 0001...
812
		if (this == DEAD_END){
1410
	if (this == DEAD_END){
813
			return "FlowInfo.DEAD_END"; //$NON-NLS-1$
1411
		return "FlowInfo.DEAD_END"; //$NON-NLS-1$
814
		}
1412
	}
815
		return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$
1413
	if ((this.tagBits & NULL_FLAG_MASK) != 0) {
816
			+", pot: " + this.potentialInits  //$NON-NLS-1$
1414
		if (this.extra == null) {
817
			+ ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
1415
			return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$
818
			+", defNull: " + this.definiteNulls  //$NON-NLS-1$
1416
				+", pot: " + this.potentialInits  //$NON-NLS-1$
819
			+", defNonNull: " + this.definiteNonNulls  //$NON-NLS-1$
1417
				+ ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
820
			+">"; //$NON-NLS-1$
1418
				+", nullS1: " + this.nullAssignmentStatusBit1 //$NON-NLS-1$
1419
				+", nullS2: " + this.nullAssignmentStatusBit2 //$NON-NLS-1$
1420
				+", nullV1: " + this.nullAssignmentValueBit1 //$NON-NLS-1$
1421
				+", nullV2: " + this.nullAssignmentValueBit2 //$NON-NLS-1$
1422
				+">"; //$NON-NLS-1$
1423
		}
1424
		else {
1425
			String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$
1426
				pot = "], pot:[" + this.potentialInits, //$NON-NLS-1$
1427
				nullS1 = ", nullS1:[" + this.nullAssignmentStatusBit1, //$NON-NLS-1$
1428
				nullS2 = "], nullS2:[" + this.nullAssignmentStatusBit2, //$NON-NLS-1$
1429
				nullV1 = "], nullV1:[" + this.nullAssignmentValueBit1, //$NON-NLS-1$
1430
				nullV2 = "], nullV2:[" + this.nullAssignmentValueBit2; //$NON-NLS-1$
1431
			int i, ceil;
1432
			for (i = 0, ceil = this.extra[0].length > 3 ? 
1433
								3 : 
1434
								this.extra[0].length;
1435
				i < ceil; i++) {
1436
				def += "," + this.extra[0][i]; //$NON-NLS-1$
1437
				pot += "," + this.extra[1][i]; //$NON-NLS-1$
1438
				nullS1 += "," + this.extra[2][i]; //$NON-NLS-1$
1439
				nullS2 += "," + this.extra[3][i]; //$NON-NLS-1$
1440
				nullV1 += "," + this.extra[4][i]; //$NON-NLS-1$
1441
				nullV2 += "," + this.extra[5][i]; //$NON-NLS-1$
1442
			}
1443
			if (ceil < this.extra[0].length) {
1444
				def += ",..."; //$NON-NLS-1$
1445
				pot += ",..."; //$NON-NLS-1$
1446
				nullS1 += ",..."; //$NON-NLS-1$
1447
				nullS2 += ",..."; //$NON-NLS-1$
1448
				nullV1 += ",..."; //$NON-NLS-1$
1449
				nullV2 += ",..."; //$NON-NLS-1$
1450
			}
1451
			return def + pot 
1452
				+ "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
1453
				+ nullS1 + nullS2 + nullV1 + nullV2
1454
				+ "]>"; //$NON-NLS-1$
1455
		}
1456
	}
1457
	else {
1458
		if (this.extra == null) {
1459
			return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$
1460
				+", pot: " + this.potentialInits  //$NON-NLS-1$
1461
				+ ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
1462
				+", no null info>"; //$NON-NLS-1$
1463
		}
1464
		else {
1465
			String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$
1466
				pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$
1467
			int i, ceil;
1468
			for (i = 0, ceil = this.extra[0].length > 3 ? 
1469
								3 : 
1470
								this.extra[0].length;
1471
				i < ceil; i++) {
1472
				def += "," + this.extra[0][i]; //$NON-NLS-1$
1473
				pot += "," + this.extra[1][i]; //$NON-NLS-1$
1474
			}
1475
			if (ceil < this.extra[0].length) {
1476
				def += ",..."; //$NON-NLS-1$
1477
				pot += ",..."; //$NON-NLS-1$
1478
			}
1479
			return def + pot 
1480
				+ "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
1481
				+ ", no null info>"; //$NON-NLS-1$
1482
		}
821
	}
1483
	}
1484
}
1485
1486
public UnconditionalFlowInfo unconditionalCopy() {
1487
	return (UnconditionalFlowInfo) copy();
1488
}
822
	
1489
	
823
	public UnconditionalFlowInfo unconditionalInits() {
1490
public UnconditionalFlowInfo unconditionalFieldLessCopy() {
824
		
1491
	// TODO (maxime) may consider leveraging null contribution verification as it is done in copy
825
		// also see conditional inits, where it requests them to merge
1492
	UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
826
		return this;
1493
	copy.tagBits = this.tagBits;
1494
	copy.maxFieldCount = this.maxFieldCount;
1495
	int limit = this.maxFieldCount;
1496
	if (limit < BitCacheSize) {
1497
		long mask;
1498
		copy.definiteInits = this.definiteInits & (mask = ~((1L << limit)-1));
1499
		copy.potentialInits = this.potentialInits & mask;
1500
		copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1 & mask;
1501
		copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2 & mask;
1502
		copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1 & mask;
1503
		copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2 & mask;
1504
	} 
1505
	// use extra vector
1506
	if (this.extra == null) {
1507
		return copy; // if vector not yet allocated, then not initialized
1508
	}
1509
	int vectorIndex, length, copyStart;
1510
	if ((vectorIndex = (limit / BitCacheSize) - 1) >= 
1511
			(length = this.extra[0].length)) {
1512
		return copy; // not enough room yet
1513
	}
1514
	long mask;
1515
	copy.extra = new long[extraLength][];
1516
	if ((copyStart = vectorIndex + 1) < length) {
1517
		int copyLength = length - copyStart;
1518
		for (int j = 0; j < extraLength; j++) {
1519
			System.arraycopy(this.extra[j], copyStart, 
1520
				(copy.extra[j] = new long[length]), copyStart, 
1521
				copyLength);
1522
		}
1523
	}
1524
	else if (vectorIndex >= 0) {
1525
		for (int j = 0; j < extraLength; j++) {
1526
			copy.extra[j] = new long[length];
1527
		}
1528
	}
1529
	if (vectorIndex >= 0) {
1530
		mask = ~((1L << (limit % BitCacheSize))-1);
1531
		for (int j = 0; j < extraLength; j++) {
1532
			copy.extra[j][vectorIndex] = 
1533
				this.extra[j][vectorIndex] & mask;
1534
		}
827
	}
1535
	}
1536
	return copy;
1537
}
1538
1539
public UnconditionalFlowInfo unconditionalInits() {
1540
	// also see conditional inits, where it requests them to merge
1541
	return this;
1542
}
1543
1544
public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() {
1545
	return this;
1546
}
828
}
1547
}
(-)compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java (+2 lines)
Lines 984-989 Link Here
984
			case 'n' :
984
			case 'n' :
985
				if ("nls".equals(warningToken)) //$NON-NLS-1$
985
				if ("nls".equals(warningToken)) //$NON-NLS-1$
986
					return NonExternalizedString;
986
					return NonExternalizedString;
987
				if ("null".equals(warningToken)) //$NON-NLS-1$
988
					return NullReference;
987
				break;
989
				break;
988
			case 's' :
990
			case 's' :
989
				if ("serial".equals(warningToken)) //$NON-NLS-1$
991
				if ("serial".equals(warningToken)) //$NON-NLS-1$
(-)compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java (-2 / +3 lines)
Lines 402-409 Link Here
402
402
403
		if (!flowInfo.isReachable()) return -1;
403
		if (!flowInfo.isReachable()) return -1;
404
404
405
		UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
405
		UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect();
406
		long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
406
		long[] extraInits = unconditionalFlowInfo.extra == null ?
407
				null : unconditionalFlowInfo.extra[0];
407
		long inits = unconditionalFlowInfo.definiteInits;
408
		long inits = unconditionalFlowInfo.definiteInits;
408
		checkNextEntry : for (int i = lastIndex; --i >= 0;) {
409
		checkNextEntry : for (int i = lastIndex; --i >= 0;) {
409
			if (definiteInits[i] == inits) {
410
			if (definiteInits[i] == inits) {
(-)compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java (+10 lines)
Lines 1374-1379 Link Here
1374
1374
1375
		case IProblem.LocalVariableCannotBeNull :
1375
		case IProblem.LocalVariableCannotBeNull :
1376
		case IProblem.LocalVariableCanOnlyBeNull :
1376
		case IProblem.LocalVariableCanOnlyBeNull :
1377
		case IProblem.LocalVariableMayBeNull :
1377
			return CompilerOptions.NullReference;
1378
			return CompilerOptions.NullReference;
1378
			
1379
			
1379
		case IProblem.BoxingConversion :
1380
		case IProblem.BoxingConversion :
Lines 3980-3985 Link Here
3980
			local.sourceEnd);
3981
			local.sourceEnd);
3981
	}
3982
	}
3982
}
3983
}
3984
public void localVariableMayBeNull(LocalVariableBinding local, ASTNode location) {
3985
	String[] arguments = new String[] {new String(local.name)};
3986
	this.handle(
3987
		IProblem.LocalVariableMayBeNull,
3988
		arguments,
3989
		arguments,
3990
		location.sourceStart,
3991
		location.sourceEnd);
3992
}
3983
public void methodMustOverride(AbstractMethodDeclaration method) {
3993
public void methodMustOverride(AbstractMethodDeclaration method) {
3984
	MethodBinding binding = method.binding;
3994
	MethodBinding binding = method.binding;
3985
	this.handle(
3995
	this.handle(
(-)compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties (+1 lines)
Lines 324-329 Link Here
324
396 = Illegal modifier for the variable {0}; only final is permitted
324
396 = Illegal modifier for the variable {0}; only final is permitted
325
397 = The variable {0} cannot be null; it was either set to a non-null value or assumed to be non-null when last used
325
397 = The variable {0} cannot be null; it was either set to a non-null value or assumed to be non-null when last used
326
398 = The variable {0} can only be null; it was either set to null or checked for null when last used
326
398 = The variable {0} can only be null; it was either set to null or checked for null when last used
327
399 = The variable {0} may be null
327
	
328
	
328
400 = The type {3} must implement the inherited abstract method {2}.{0}({1})
329
400 = The type {3} must implement the inherited abstract method {2}.{0}({1})
329
401 = Cannot override the final method from {0}
330
401 = Cannot override the final method from {0}

Return to bug 110030