blob: 3b0e7eb124d22d4ec36fb398efae43a15d687f9b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
import org.eclipse.jdt.internal.core.Assert.AssertionFailedException; // for coverage tests
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
/**
* Record initialization status during definite assignment analysis
*
* No caching of pre-allocated instances.
*/
public class UnconditionalFlowInfo extends FlowInfo {
// Coverage tests
// Coverage tests need that the code be instrumented. The following flag
// controls whether the instrumented code is compiled in or not, and whether
// the coverage tests methods run or not.
public final static boolean coverageTestFlag = false;
// never release with the coverageTestFlag set to true
public static int coverageTestId;
public long definiteInits;
public long potentialInits;
public long nullAssignmentStatusBit1;
public long nullAssignmentStatusBit2;
// 0 0 is potential (bit 1 is leftmost here)
// 1 0 is assigned
// 0 1 is protected null (aka if (o == null) { // here o protected null...)
// 1 1 is protected non null
public long nullAssignmentValueBit1;
public long nullAssignmentValueBit2;
// information only relevant for potential and assigned
// 0 0 is start -- nothing known at all
// 0 1 is assigned non null or potential anything but null
// 1 0 is assigned null or potential null
// 1 1 is potential null and potential anything but null or definite unknown
// REVIEW consider reintroducing the difference between potential non null and potential
// REVIEW unknown; if this is done, rename to nullAssignmentBit[1-4] since the semantics
// REVIEW would be ever less clear
// REVIEW went public in order to grant access to tests; do not like it...
public static final int extraLength = 6;
public long extra[][];
// extra bit fields for larger numbers of fields/variables
// extra[0] holds definiteInits values, extra[1] potentialInits, etc.
// lifecycle is extra == null or else all extra[]'s are allocated
// arrays which have the same size
public int maxFieldCount; // limit between fields and locals
// Constants
public static final int BitCacheSize = 64; // 64 bits in a long.
public FlowInfo addInitializationsFrom(FlowInfo inits) {
if (this == DEAD_END)
return this;
if (inits == DEAD_END)
return this;
UnconditionalFlowInfo otherInits = inits.unconditionalInits();
// union of definitely assigned variables,
this.definiteInits |= otherInits.definiteInits;
// union of potentially set ones
this.potentialInits |= otherInits.potentialInits;
// combine null information
// note: we may have both forms of protection (null and non null)
// coming with otherInits, because of loops
boolean considerNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0;
long a1, na1, a2, na2, a3, a4, na4, b1, b2, nb2, b3, nb3, b4, nb4;
// REVIEW does an inner declaration save stack space? does duplicate declaration waste time?
if (considerNulls) {
if ((this.tagBits & NULL_FLAG_MASK) == 0) {
this.nullAssignmentStatusBit1 = otherInits.nullAssignmentStatusBit1;
this.nullAssignmentStatusBit2 = otherInits.nullAssignmentStatusBit2;
this.nullAssignmentValueBit1 = otherInits.nullAssignmentValueBit1;
this.nullAssignmentValueBit2 = otherInits.nullAssignmentValueBit2;
if (coverageTestFlag && coverageTestId == 1) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// TODO (maxime) indent as follows:
/*
* a
* | (b
* & c)
*
*/
// REVIEW indentation example
this.nullAssignmentStatusBit1 =
(b1 = otherInits.nullAssignmentStatusBit1)
| ((a1 = this.nullAssignmentStatusBit1)
& (((nb2 = ~(b2 = otherInits.nullAssignmentStatusBit2))
& (nb3 = ~(b3 = otherInits.nullAssignmentValueBit1))
& ((nb4 = ~(b4 = otherInits.nullAssignmentValueBit2))
| ((a2 = this.nullAssignmentStatusBit2)
^ (a4 = this.nullAssignmentValueBit2))))
| nb4 & (na2 = ~a2) & (na4 = ~a4)));
this.nullAssignmentStatusBit2 =
(b1 & b2)
| (~b1
& ((((na1 = ~a1) | a4) & b2)
| (a2
& (b2
| (a1 & (na4 = ~a4) & nb2 & nb3)
| ((~(a3 = this.nullAssignmentValueBit1) & nb3)
| (na1 & na4))
& nb4))));
this.nullAssignmentValueBit1 =
nb2 & b3 |
~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) |
a1 & na2 & a3 & nb2 |
(a1 | a2 | na4) & b3);
this.nullAssignmentValueBit2 =
b4 |
a4 & (nb2 & nb3 | ~(b1 ^ b2));
if (coverageTestFlag && coverageTestId == 2) {
this.nullAssignmentValueBit2 = ~0;
}
}
this.tagBits |= NULL_FLAG_MASK; // in all cases - avoid forgetting extras
}
// treating extra storage
if (this.extra != null || otherInits.extra != null) {
int mergeLimit = 0, copyLimit = 0;
if (this.extra != null) {
if (otherInits.extra != null) {
// both sides have extra storage
int length, otherLength;
if ((length = this.extra[0].length) <
(otherLength = otherInits.extra[0].length)) {
if (coverageTestFlag && coverageTestId == 3) {
throw new AssertionFailedException("COVERAGE 3"); //$NON-NLS-1$
}
// current storage is shorter -> grow current
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[otherLength]), 0, length);
}
mergeLimit = length;
copyLimit = otherLength;
} else {
if (coverageTestFlag && coverageTestId == 4) {
throw new AssertionFailedException("COVERAGE 4"); //$NON-NLS-1$
}
// current storage is longer
mergeLimit = otherLength;
}
}
}
else if (otherInits.extra != null) {
// no storage here, but other has extra storage.
// shortcut regular copy because array copy is better
int otherLength;
this.extra = new long[extraLength][];
System.arraycopy(otherInits.extra[0], 0,
(this.extra[0] = new long[otherLength =
otherInits.extra[0].length]), 0, otherLength);
System.arraycopy(otherInits.extra[1], 0,
(this.extra[1] = new long[otherLength]), 0, otherLength);
if (considerNulls) {
for (int j = 2; j < extraLength; j++) {
System.arraycopy(otherInits.extra[j], 0,
(this.extra[j] = new long[otherLength]), 0, otherLength);
}
if (coverageTestFlag && coverageTestId == 5) {
this.extra[5][otherLength - 1] = ~0;
}
}
else {
for (int j = 2; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
if (coverageTestFlag && coverageTestId == 6) {
this.extra[5][otherLength - 1] = ~0;
}
}
}
int i = 0;
for (; i < mergeLimit; i++) {
this.extra[0][i] |= otherInits.extra[0][i];
this.extra[1][i] |= otherInits.extra[1][i];
if (considerNulls) { // could consider pushing the test outside the loop
if (this.extra[2][i] == 0 &&
this.extra[3][i] == 0 &&
this.extra[4][i] == 0 &&
this.extra[5][i] == 0) {
for (int j = 2; j < extraLength; j++) {
this.extra[j][i] = otherInits.extra[j][i];
}
if (coverageTestFlag && coverageTestId == 7) {
this.extra[5][i] = ~0;
}
}
else {
this.extra[2][i] =
(b1 = otherInits.extra[2][i]) |
(a1 = this.extra[2][i]) &
((nb2 = ~(b2 = otherInits.extra[3][i])) &
(nb3 = ~(b3 = otherInits.extra[4][i])) &
((nb4 = ~(b4 = otherInits.extra[5][i])) |
((a2 = this.extra[3][i]) ^
(a4 = this.extra[5][i]))) |
nb4 & (na2 = ~a2) & (na4 = ~a4));
this.extra[3][i] =
b1 & b2 |
~b1 & (((na1 = ~a1) | a4) & b2 |
a2 & (b2 |
a1 & (na4 = ~a4) & nb2 & nb3 |
(~(a3 = this.extra[4][i]) & nb3 | na1 & na4) & nb4));
this.extra[4][i] =
nb2 & b3 |
~b1 & ((a1 & na2 & na4 | na1 & a3) & (nb2 | nb4) |
a1 & na2 & a3 & nb2 |
(a1 | a2 | na4) & b3);
this.extra[5][i] =
b4 |
a4 & (nb2 & nb3 | ~(b1 ^ b2));
if (coverageTestFlag && coverageTestId == 8) {
this.extra[5][i] = ~0;
}
}
}
}
for (; i < copyLimit; i++) {
this.extra[0][i] = otherInits.extra[0][i];
this.extra[1][i] = otherInits.extra[1][i];
if (considerNulls) {
for (int j = 2; j < extraLength; j++) {
this.extra[j][i] = otherInits.extra[j][i];
}
if (coverageTestFlag && coverageTestId == 9) {
this.extra[5][i] = ~0;
}
}
}
}
return this;
}
public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
if (this == DEAD_END){
return this;
}
if (inits == DEAD_END){
return this;
}
UnconditionalFlowInfo otherInits = inits.unconditionalInits();
// union of potentially set ones
this.potentialInits |= otherInits.potentialInits;
// treating extra storage
if (this.extra != null) {
if (otherInits.extra != null) {
// both sides have extra storage
int i = 0, length, otherLength;
if ((length = this.extra[0].length) < (otherLength = otherInits.extra[0].length)) {
// current storage is shorter -> grow current
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[otherLength]), 0, length);
}
for (; i < length; i++) {
this.extra[1][i] |= otherInits.extra[1][i];
}
for (; i < otherLength; i++) {
this.extra[1][i] = otherInits.extra[1][i];
}
}
else {
// current storage is longer
for (; i < otherLength; i++) {
this.extra[1][i] |= otherInits.extra[1][i];
}
}
}
}
else if (otherInits.extra != null) {
// no storage here, but other has extra storage.
int otherLength = otherInits.extra[0].length;
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
System.arraycopy(otherInits.extra[1], 0, this.extra[1], 0,
otherLength);
}
this.addPotentialNullInfoFrom(otherInits);
// REVIEW inline?
return this;
}
/**
* Compose other inits over this flow info, then return this. The operation
* semantics are to wave into this flow info the consequences upon null
* information of a possible path into the operations that resulted into
* otherInits. The fact that this path may be left unexecuted under peculiar
* conditions results into less specific results than
* {@link #addInitializationsFrom(FlowInfo) addInitializationsFrom}; moreover,
* only the null information is affected.
* @param otherInits other null inits to compose over this
* @return this, modified according to otherInits information
*/
public UnconditionalFlowInfo addPotentialNullInfoFrom(
UnconditionalFlowInfo otherInits) {
if ((this.tagBits & UNREACHABLE) != 0 ||
(otherInits.tagBits & UNREACHABLE) != 0 ||
(otherInits.tagBits & NULL_FLAG_MASK) == 0) {
return this;
}
// if we get here, otherInits has some null info
boolean thisHasNulls = (this.tagBits & NULL_FLAG_MASK) != 0;
if (thisHasNulls) {
long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4;
this.nullAssignmentStatusBit1 =
((a1 = this.nullAssignmentStatusBit1) &
(na4 = ~(a4 = this.nullAssignmentValueBit2)) &
((na3 = ~(a3 = this.nullAssignmentValueBit1)) |
(a2 = this.nullAssignmentStatusBit2)) |
a2 & na3 & a4) &
(nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) &
((b2 = otherInits.nullAssignmentStatusBit2) |
(nb4 = ~(b4 = otherInits.nullAssignmentValueBit2))) |
a1 & (na2 = ~a2) &
(a4 & ((nb1 = ~(b1 = otherInits.nullAssignmentStatusBit1)) &
nb3 | b1 &
(b4 | b2)) |
na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) |
b1 & nb4 & (nb2 | nb3)));
this.nullAssignmentStatusBit2 =
a2 & (~a1 & na4 & nb4 |
a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) |
b1 & (nb4 |b2 & b4)));
this.nullAssignmentValueBit1 =
a3 |
b1 & nb2 & nb4 |
nb1 & b3 |
a1 & na2 & (b1 & b3 | nb1 & b4);
// b1 & (~b2 & ~b4 | a1 & ~a2 & b3) |
// ~b1 & (b3 | a1 & ~a2 & b4); -- same op nb
this.nullAssignmentValueBit2 =
a4 & (na2 | a2 & na3) |
b4 & (nb2 | b2 & nb3);
if (coverageTestFlag && coverageTestId == 15) {
this.nullAssignmentValueBit2 = ~0;
}
// extra storage management
if (otherInits.extra != null) {
int mergeLimit = 0, copyLimit = 0;
int otherLength = otherInits.extra[0].length;
if (this.extra == null) {
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
copyLimit = otherLength;
if (coverageTestFlag && coverageTestId == 16) {
this.extra[2][0] = ~0; thisHasNulls = true;
}
}
else {
mergeLimit = otherLength;
if (mergeLimit > this.extra[0].length) {
copyLimit = mergeLimit;
mergeLimit = this.extra[0].length;
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
this.extra[j] = new long[otherLength], 0,
mergeLimit);
}
}
int i;
for (i = 0; i < mergeLimit; i++) {
this.extra[2][i] =
((a1 = this.extra[2][i]) &
(na4 = ~(a4 = this.extra[5][i])) &
((na3 = ~(a3 = this.extra[4][i])) |
(a2 = this.extra[3][i])) |
a2 & na3 & a4) &
(nb3 = ~(b3 = otherInits.extra[4][i])) &
((b2 = otherInits.extra[3][i]) |
(nb4 = ~(b4 = otherInits.extra[5][i]))) |
a1 & (na2 = ~a2) &
(a4 & ((nb1 = ~(b1 = otherInits.extra[2][i])) &
nb3 | b1 &
(b4 | b2)) |
na4 & (nb1 & (((nb2 = ~b2) & nb4 | b2) & nb3 | b3 & nb4) |
b1 & nb4 & (nb2 | nb3)));
this.extra[3][i] =
a2 & (~a1 & na4 & nb4 |
a1 & na3 & nb3 & (nb1 & (nb2 & nb4 | b2) |
b1 & (nb4 |b2 & b4)));
this.extra[4][i] =
a3 |
b1 & nb2 & nb4 |
nb1 & b3 |
a1 & na2 & (b1 & b3 | nb1 & b4);
this.extra[5][i] =
a4 & (na2 | a2 & na3) |
b4 & (nb2 | b2 & nb3);
if (coverageTestFlag && coverageTestId == 17) {
this.nullAssignmentValueBit2 = ~0;
}
}
for (; i < copyLimit; i++) {
if (otherInits.extra[4][i] != 0 ||
otherInits.extra[5][i] != 0) {
this.tagBits |= NULL_FLAG_MASK;
this.extra[4][i] =
otherInits.extra[4][i] &
~(otherInits.extra[2][i] &
~otherInits.extra[3][i] &
otherInits.extra[5][i]);
this.extra[5][i] =
otherInits.extra[5][i];
if (coverageTestFlag && coverageTestId == 18) {
this.extra[5][i] = ~0;
}
}
}
}
}
}
else {
if (otherInits.nullAssignmentValueBit1 != 0 ||
otherInits.nullAssignmentValueBit2 != 0) {
// add potential values
this.nullAssignmentValueBit1 =
otherInits.nullAssignmentValueBit1 &
~(otherInits.nullAssignmentStatusBit1 &
~otherInits.nullAssignmentStatusBit2 &
otherInits.nullAssignmentValueBit2); // exclude assigned unknown
this.nullAssignmentValueBit2 =
otherInits.nullAssignmentValueBit2;
thisHasNulls =
this.nullAssignmentValueBit1 != 0 ||
this.nullAssignmentValueBit2 != 0;
if (coverageTestFlag && coverageTestId == 10) {
this.nullAssignmentValueBit2 = ~0;
}
}
// extra storage management
if (otherInits.extra != null) {
int mergeLimit = 0, copyLimit = 0;
int otherLength = otherInits.extra[0].length;
if (this.extra == null) {
copyLimit = otherLength;
// cannot happen when called from addPotentialInitializationsFrom
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
if (coverageTestFlag && coverageTestId == 11) {
this.extra[5][0] = ~0; this.tagBits |= NULL_FLAG_MASK;
}
}
else {
mergeLimit = otherLength;
if (mergeLimit > this.extra[0].length) {
copyLimit = mergeLimit;
mergeLimit = this.extra[0].length;
System.arraycopy(this.extra[0], 0,
this.extra[0] = new long[otherLength], 0,
mergeLimit);
System.arraycopy(this.extra[1], 0,
this.extra[1] = new long[otherLength], 0,
mergeLimit);
for (int j = 2; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
if (coverageTestFlag && coverageTestId == 12) {
throw new AssertionFailedException("COVERAGE 12"); //$NON-NLS-1$
}
}
}
int i;
for (i = 0; i < mergeLimit; i++) {
if (otherInits.extra[4][i] != 0 ||
otherInits.extra[5][i] != 0) {
this.extra[4][i] |=
otherInits.extra[4][i] &
~(otherInits.extra[2][i] &
~otherInits.extra[3][i] &
otherInits.extra[5][i]);
this.extra[5][i] |=
otherInits.extra[5][i];
thisHasNulls = thisHasNulls ||
this.extra[4][i] != 0 ||
this.extra[5][i] != 0;
if (coverageTestFlag && coverageTestId == 13) {
this.extra[5][i] = ~0;
}
}
}
for (; i < copyLimit; i++) {
if (otherInits.extra[4][i] != 0 ||
otherInits.extra[5][i] != 0) {
this.extra[4][i] =
otherInits.extra[4][i] &
~(otherInits.extra[2][i] &
~otherInits.extra[3][i] &
otherInits.extra[5][i]);
this.extra[5][i] =
otherInits.extra[5][i];
thisHasNulls = thisHasNulls ||
this.extra[4][i] != 0 ||
this.extra[5][i] != 0;
if (coverageTestFlag && coverageTestId == 14) {
this.extra[5][i] = ~0;
}
}
}
}
}
if (thisHasNulls) {
this.tagBits |= NULL_FLAG_MASK;
}
else {
this.tagBits &= NULL_FLAG_MASK;
}
return this;
}
public FlowInfo copy() {
// do not clone the DeadEnd
if (this == DEAD_END) {
return this;
}
UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
// copy slots
copy.definiteInits = this.definiteInits;
copy.potentialInits = this.potentialInits;
boolean hasNullInfo = (this.tagBits & NULL_FLAG_MASK) != 0;
if (hasNullInfo) {
copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1;
copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2;
copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1;
copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2;
}
copy.tagBits = this.tagBits;
copy.maxFieldCount = this.maxFieldCount;
if (this.extra != null) {
int length;
copy.extra = new long[extraLength][];
System.arraycopy(this.extra[0], 0,
(copy.extra[0] = new long[length = this.extra[0].length]), 0,
length);
System.arraycopy(this.extra[1], 0,
(copy.extra[1] = new long[length]), 0, length);
if (hasNullInfo) {
for (int j = 2; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(copy.extra[j] = new long[length]), 0, length);
}
}
else {
for (int j = 2; j < extraLength; j++) {
copy.extra[j] = new long[length];
}
}
}
return copy;
}
/**
* Remove local variables information from this flow info and return this.
* @return this, deprived from any local variable information
*/
public UnconditionalFlowInfo discardNonFieldInitializations() {
int limit = this.maxFieldCount;
if (limit < BitCacheSize) {
long mask = (1L << limit)-1;
this.definiteInits &= mask;
this.potentialInits &= mask;
this.nullAssignmentStatusBit1 &= mask;
this.nullAssignmentStatusBit2 &= mask;
this.nullAssignmentValueBit1 &= mask;
this.nullAssignmentValueBit2 &= mask;
}
// use extra vector
if (this.extra == null) {
return this; // if vector not yet allocated, then not initialized
}
int vectorIndex, length = this.extra[0].length;
if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
return this; // not enough room yet
}
if (vectorIndex >= 0) {
// else we only have complete non field array items left
long mask = (1L << (limit % BitCacheSize))-1;
for (int j = 0; j < extraLength; j++) {
this.extra[j][vectorIndex] &= mask;
}
}
for (int i = vectorIndex + 1; i < length; i++) {
for (int j = 0; j < extraLength; j++) {
this.extra[j][i] = 0;
}
}
return this;
}
public FlowInfo initsWhenFalse() {
return this;
}
public FlowInfo initsWhenTrue() {
return this;
}
/**
* Check status of definite assignment at a given position.
* It deals with the dual representation of the InitializationInfo2:
* bits for the first 64 entries, then an array of booleans.
*/
final private boolean isDefinitelyAssigned(int position) {
if (position < BitCacheSize) {
// use bits
return (this.definiteInits & (1L << position)) != 0;
}
// use extra vector
if (this.extra == null)
return false; // if vector not yet allocated, then not initialized
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1)
>= this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return ((this.extra[0][vectorIndex]) &
(1L << (position % BitCacheSize))) != 0;
}
final public boolean isDefinitelyAssigned(FieldBinding field) {
// Mirrored in CodeStream.isDefinitelyAssigned(..)
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0) {
return true;
}
return isDefinitelyAssigned(field.id);
}
final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0) {
return true;
}
// final constants are inlined, and thus considered as always initialized
if (local.constant() != Constant.NotAConstant) {
return true;
}
return isDefinitelyAssigned(local.id + this.maxFieldCount);
}
final public boolean isDefinitelyNonNull(LocalVariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
return false;
}
if ((local.type.tagBits & TagBits.IsBaseType) != 0 ||
local.constant() != Constant.NotAConstant) {
// REVIEW only true if local is of a non object type, hence
// REVIEW second test is useless?
return true;
}
int position = local.id + this.maxFieldCount;
long mask;
if (position < BitCacheSize) { // use bits
return
(this.nullAssignmentStatusBit2 &
(mask = 1L << position)) != 0 ?
(this.nullAssignmentStatusBit1 & mask) != 0 :
(this.nullAssignmentStatusBit1 &
this.nullAssignmentValueBit2 & mask) != 0 &&
(this.nullAssignmentValueBit1 & mask) == 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1)
>= this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return
(this.extra[3][vectorIndex] &
(mask = 1L << (position % BitCacheSize))) != 0 ?
(this.extra[2][vectorIndex] & mask) != 0 :
(this.extra[2][vectorIndex] &
this.extra[5][vectorIndex] & mask) != 0 &&
(this.extra[4][vectorIndex] & mask) == 0;
}
final public boolean isDefinitelyNull(LocalVariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
int position = local.id + this.maxFieldCount;
long mask;
if (position < BitCacheSize) { // use bits
return
(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
(this.nullAssignmentStatusBit1 & mask) == 0 :
(this.nullAssignmentStatusBit1 &
this.nullAssignmentValueBit1 & mask) != 0 &&
(this.nullAssignmentValueBit2 & mask) == 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return
(this.extra[3][vectorIndex] &
(mask = 1L << (position % BitCacheSize))) != 0 ?
(this.extra[2][vectorIndex] & mask) == 0 :
(this.extra[2][vectorIndex] &
this.extra[4][vectorIndex] & mask) != 0 &&
(this.extra[5][vectorIndex] & mask) == 0;
}
final public boolean isDefinitelyUnknown(LocalVariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
return false;
}
int position = local.id + this.maxFieldCount;
long mask;
if (position < BitCacheSize) { // use bits
return
(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
false :
(this.nullAssignmentStatusBit1 &
this.nullAssignmentValueBit1 &
this.nullAssignmentValueBit2 & mask) != 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return
(this.extra[3][vectorIndex] &
(mask = 1L << (position % BitCacheSize))) != 0 ?
false :
(this.extra[2][vectorIndex] &
this.extra[4][vectorIndex] &
this.extra[5][vectorIndex] &
mask) != 0;
}
/**
* Check status of potential assignment at a given position.
*/
final private boolean isPotentiallyAssigned(int position) {
// id is zero-based
if (position < BitCacheSize) {
// use bits
return (this.potentialInits & (1L << position)) != 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1)
>= this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return ((this.extra[1][vectorIndex]) &
(1L << (position % BitCacheSize))) != 0;
}
/**
* REVIEW wrong comment?
* Check status of definite assignment for a field.
*/
final public boolean isPotentiallyAssigned(FieldBinding field) {
return isPotentiallyAssigned(field.id);
}
/**
* Check status of potential assignment for a local.
*/
final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
// final constants are inlined, and thus considered as always initialized
if (local.constant() != Constant.NotAConstant) {
return true;
}
return isPotentiallyAssigned(local.id + this.maxFieldCount);
}
// REVIEW should rename this -- what we do is that we ask if there is a reasonable
// REVIEW expectation that the variable be null at this point; which means that
// REVIEW we add the protected null case, to augment diagnostics, but we do not
// REVIEW really check that someone deliberately has assigned to null on a given
// REVIEW path
final public boolean isPotentiallyNull(LocalVariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
int position;
long mask;
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
return
(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
(this.nullAssignmentStatusBit1 & mask) == 0 : // protected null
(this.nullAssignmentValueBit1 & mask) != 0 && // null bit set and
((this.nullAssignmentStatusBit1 & mask) == 0 || // (potential or
(this.nullAssignmentValueBit2 & mask) == 0);
// assigned, but not unknown)
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return
(this.extra[3][vectorIndex] &
(mask = 1L << (position % BitCacheSize))) != 0 ?
(this.extra[2][vectorIndex] & mask) == 0 :
(this.extra[4][vectorIndex] & mask) != 0 &&
((this.extra[2][vectorIndex] & mask) == 0 ||
(this.extra[5][vectorIndex] & mask) == 0);
}
final public boolean isPotentiallyUnknown(LocalVariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
return false;
}
int position = local.id + this.maxFieldCount;
long mask;
if (position < BitCacheSize) { // use bits
return
(this.nullAssignmentStatusBit2 & (mask = 1L << position)) != 0 ?
false :
((this.nullAssignmentStatusBit1 &
this.nullAssignmentValueBit1 |
~this.nullAssignmentStatusBit1 &
~this.nullAssignmentValueBit1) &
this.nullAssignmentValueBit2 & mask) != 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return
(this.extra[3][vectorIndex] &
(mask = 1L << (position % BitCacheSize))) != 0 ?
false :
((this.extra[2][vectorIndex] &
this.extra[4][vectorIndex] |
~this.extra[2][vectorIndex] &
~this.extra[4][vectorIndex]) &
this.extra[5][vectorIndex] &
mask) != 0;
}
final public boolean isProtectedNonNull(LocalVariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
int position;
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
return (this.nullAssignmentStatusBit1 &
this.nullAssignmentStatusBit2 & (1L << position)) != 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return (this.extra[4][vectorIndex] &
this.extra[5][vectorIndex] &
(1L << (position % BitCacheSize))) != 0;
}
final public boolean isProtectedNull(LocalVariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
int position;
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
return (~this.nullAssignmentStatusBit1 &
this.nullAssignmentStatusBit2 & (1L << position)) != 0;
}
// use extra vector
if (this.extra == null) {
return false; // if vector not yet allocated, then not initialized
}
int vectorIndex;
if ((vectorIndex = (position / BitCacheSize) - 1) >=
this.extra[0].length) {
return false; // if not enough room in vector, then not initialized
}
return (~this.extra[4][vectorIndex] &
this.extra[5][vectorIndex] &
(1L << (position % BitCacheSize))) != 0;
}
public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
long mask;
// position is zero-based
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
if (((mask = 1L << position) & // leave assigned non null unchanged
this.nullAssignmentStatusBit1 &
~this.nullAssignmentStatusBit2 &
~this.nullAssignmentValueBit1 &
this.nullAssignmentValueBit2) == 0) {
// set protected non null
this.nullAssignmentStatusBit1 |= mask;
this.nullAssignmentStatusBit2 |= mask;
// clear potential null
this.nullAssignmentValueBit1 &= ~mask;
if (coverageTestFlag && coverageTestId == 19) {
this.nullAssignmentValueBit2 = ~0;
}
}
if (coverageTestFlag && coverageTestId == 20) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
if (this.extra == null) {
int length = vectorIndex + 1;
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[length];
}
if (coverageTestFlag && coverageTestId == 21) {
throw new AssertionFailedException("COVERAGE 21"); //$NON-NLS-1$
}
}
else {
int oldLength;
if (vectorIndex >= (oldLength = this.extra[0].length)) {
int newLength = vectorIndex + 1;
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[newLength]), 0,
oldLength);
}
if (coverageTestFlag && coverageTestId == 22) {
throw new AssertionFailedException("COVERAGE 22"); //$NON-NLS-1$
}
}
}
if (((mask = 1L << (position % BitCacheSize)) &
this.extra[2][vectorIndex] &
~this.extra[3][vectorIndex] &
~this.extra[4][vectorIndex] &
this.extra[5][vectorIndex]) == 0) {
this.extra[2][vectorIndex] |= mask;
this.extra[3][vectorIndex] |= mask;
this.extra[4][vectorIndex] &= ~mask;
if (coverageTestFlag && coverageTestId == 23) {
this.extra[5][vectorIndex] = ~0;
}
}
}
}
}
// REVIEW javadoc policy?
public void markAsComparedEqualToNull(LocalVariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
long mask, unknownAssigned;
// position is zero-based
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
mask = 1L << position;
if ((mask & // leave assigned null unchanged
this.nullAssignmentStatusBit1 &
~this.nullAssignmentStatusBit2 &
this.nullAssignmentValueBit1 &
~this.nullAssignmentValueBit2) == 0) {
unknownAssigned = this.nullAssignmentStatusBit1 &
~this.nullAssignmentStatusBit2 &
this.nullAssignmentValueBit1 &
this.nullAssignmentValueBit2;
// set protected
this.nullAssignmentStatusBit2 |= mask;
this.nullAssignmentStatusBit1 &= (mask = ~mask);
// protected is null
this.nullAssignmentValueBit1 &= mask | ~unknownAssigned;
this.nullAssignmentValueBit2 &= mask;
// clear potential anything but null
// REVIEW coûts relatifs d'un assignment et d'une négation?
if (coverageTestFlag && coverageTestId == 24) {
this.nullAssignmentValueBit2 = ~0;
}
}
if (coverageTestFlag && coverageTestId == 25) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
mask = 1L << (position % BitCacheSize);
if (this.extra == null) {
int length = vectorIndex + 1;
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[length ];
}
if (coverageTestFlag && coverageTestId == 26) {
throw new AssertionFailedException("COVERAGE 26"); //$NON-NLS-1$
}
}
else {
int oldLength;
if (vectorIndex >= (oldLength = this.extra[0].length)) {
int newLength = vectorIndex + 1;
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[newLength]), 0,
oldLength);
}
if (coverageTestFlag && coverageTestId == 27) {
throw new AssertionFailedException("COVERAGE 27"); //$NON-NLS-1$
}
}
}
if ((mask &
this.extra[2][vectorIndex] &
~this.extra[3][vectorIndex] &
this.extra[4][vectorIndex] &
~this.extra[5][vectorIndex]) == 0) {
unknownAssigned = this.extra[2][vectorIndex] &
~this.extra[3][vectorIndex] &
this.extra[4][vectorIndex] &
this.extra[5][vectorIndex];
this.extra[3][vectorIndex] |= mask;
this.extra[2][vectorIndex] &= (mask = ~mask);
this.extra[4][vectorIndex] &= mask | ~unknownAssigned;
this.extra[5][vectorIndex] &= mask;
if (coverageTestFlag && coverageTestId == 28) {
this.extra[5][vectorIndex] = ~0;
}
}
}
}
}
/**
* Record a definite assignment at a given position.
* REVIEW wrong comment?
* It deals with the dual representation of the InitializationInfo2:
* bits for the first 64 entries, then an array of booleans.
*/
final private void markAsDefinitelyAssigned(int position) {
if (this != DEAD_END) {
// position is zero-based
if (position < BitCacheSize) {
// use bits
long mask;
this.definiteInits |= (mask = 1L << position);
this.potentialInits |= mask;
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
if (this.extra == null) {
int length = vectorIndex + 1;
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[length];
}
}
else {
int oldLength; // might need to grow the arrays
if (vectorIndex >= (oldLength = this.extra[0].length)) {
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[vectorIndex + 1]), 0,
oldLength);
}
}
}
long mask;
this.extra[0][vectorIndex] |=
(mask = 1L << (position % BitCacheSize));
this.extra[1][vectorIndex] |= mask;
}
}
}
public void markAsDefinitelyAssigned(FieldBinding field) {
if (this != DEAD_END)
markAsDefinitelyAssigned(field.id);
}
public void markAsDefinitelyAssigned(LocalVariableBinding local) {
if (this != DEAD_END)
markAsDefinitelyAssigned(local.id + this.maxFieldCount);
}
/**
* Record a definite non-null assignment at a given position.
*/
final private void markAsDefinitelyNonNull(int position) {
// DEAD_END guarded above
this.tagBits |= NULL_FLAG_MASK;
long mask;
// position is zero-based
if (position < BitCacheSize) {
// use bits
this.nullAssignmentStatusBit1 |= (mask = 1L << position);
this.nullAssignmentValueBit2 |= mask; // set non null
this.nullAssignmentStatusBit2 &= ~mask; // clear protection
this.nullAssignmentValueBit1 &= ~mask; // clear null
if (coverageTestFlag && coverageTestId == 29) {
this.nullAssignmentStatusBit1 = 0;
}
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
// REVIEW seems to be guarded
this.extra[2][vectorIndex] |=
(mask = 1L << (position % BitCacheSize));
this.extra[5][vectorIndex] |= mask;
this.extra[3][vectorIndex] &= ~mask;
this.extra[4][vectorIndex] &= ~mask;
if (coverageTestFlag && coverageTestId == 30) {
this.extra[5][vectorIndex] = ~0;
}
}
}
public void markAsDefinitelyNonNull(FieldBinding field) {
if (this != DEAD_END) {
markAsDefinitelyNonNull(field.id);
}
}
public void markAsDefinitelyNonNull(LocalVariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
markAsDefinitelyNonNull(local.id + this.maxFieldCount);
}
}
/**
* Record a definite null assignment at a given position.
*/
final private void markAsDefinitelyNull(int position) {
// DEAD_END guarded above
this.tagBits |= NULL_FLAG_MASK;
long mask;
if (position < BitCacheSize) {
// use bits
this.nullAssignmentStatusBit1 |= (mask = 1L << position); // set assignment
this.nullAssignmentStatusBit2 &= ~mask; // clear protection
this.nullAssignmentValueBit1 |= mask; // set null
this.nullAssignmentValueBit2 &= ~mask; // clear non null
if (coverageTestFlag && coverageTestId == 31) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
// REVIEW seems to be guarded
this.extra[2][vectorIndex] |=
(mask = 1L << (position % BitCacheSize));
this.extra[3][vectorIndex] &= ~mask;
this.extra[4][vectorIndex] |= mask;
this.extra[5][vectorIndex] &= ~mask;
if (coverageTestFlag && coverageTestId == 32) {
this.extra[5][vectorIndex] = ~0;
}
}
}
public void markAsDefinitelyNull(FieldBinding field) {
if (this != DEAD_END) {
markAsDefinitelyNull(field.id);
}
}
public void markAsDefinitelyNull(LocalVariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
markAsDefinitelyNull(local.id + this.maxFieldCount);
}
}
/**
* Mark a local as having been assigned to an unknown value.
* @param local the local to mark
*/
// PREMATURE may try to get closer to markAsDefinitelyAssigned, but not
// obvious
public void markAsDefinitelyUnknown(LocalVariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
long mask;
int position;
// position is zero-based
if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
// use bits
this.nullAssignmentValueBit1 |= (mask = 1L << position);
this.nullAssignmentValueBit2 |= mask;
// set unknown
this.nullAssignmentStatusBit1 |= mask;
// set assignment
this.nullAssignmentStatusBit2 &= ~mask;
// clear protection
if (coverageTestFlag && coverageTestId == 33) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
// REVIEW seems to be guarded
this.extra[4][vectorIndex] |=
(mask = 1L << (position % BitCacheSize));
this.extra[5][vectorIndex] |= mask;
this.extra[2][vectorIndex] |= mask;
this.extra[3][vectorIndex] &= ~mask;
if (coverageTestFlag && coverageTestId == 34) {
this.extra[5][vectorIndex] = ~0;
}
}
}
}
public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
if ((otherInits.tagBits & UNREACHABLE) != 0 && this != DEAD_END) {
if (coverageTestFlag && coverageTestId == 35) {
throw new AssertionFailedException("COVERAGE 35"); //$NON-NLS-1$
}
// DEAD_END + unreachable other -> other
return this;
}
if ((this.tagBits & UNREACHABLE) != 0) {
if (coverageTestFlag && coverageTestId == 36) {
throw new AssertionFailedException("COVERAGE 36"); //$NON-NLS-1$
}
return (UnconditionalFlowInfo) otherInits.copy(); // make sure otherInits won't be affected
}
// intersection of definitely assigned variables,
this.definiteInits &= otherInits.definiteInits;
// union of potentially set ones
this.potentialInits |= otherInits.potentialInits;
// null combinations
boolean otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0,
thisHasNulls = false;
long a1, a2, na2, a3, na3, a4, na4, b1, nb1, b2, nb2, b3, nb3, b4, nb4;
if (otherHasNulls) {
this.nullAssignmentStatusBit1 =
(a1 = this.nullAssignmentStatusBit1) &
(b1 = otherInits.nullAssignmentStatusBit1) & (
(nb4 = ~(b4 = otherInits.nullAssignmentValueBit2)) &
((b2 = otherInits.nullAssignmentStatusBit2) &
(nb3 = ~(b3 = otherInits.nullAssignmentValueBit1)) &
(na3 = ~(a3 = this.nullAssignmentValueBit1)) &
((a2 = this.nullAssignmentStatusBit2) &
(na4 = ~(a4 = this.nullAssignmentValueBit2)) | a4) |
(na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) |
b4 & (na3 & nb3 & (na4 & a2 | a4) |
na2 & a4 & nb2));
this.nullAssignmentStatusBit2 =
a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) |
a1 & b1 & (a2 ^ b2) & na3 & nb3 |
(a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4;
this.nullAssignmentValueBit1 =
b1 & nb2 & nb4 |
~a1 & (a3 |
a2 & na3 & (b1 | nb2)) |
(a1 | na2) & nb1 & b2 & nb3 |
nb1 & b3 |
a1 & na2 & (na4 |
b1 & nb2 & (a3 | b3));
this.nullAssignmentValueBit2 =
a4 | b4;
if (coverageTestFlag && coverageTestId == 37) {
this.nullAssignmentValueBit2 = ~0;
}
}
else {
// tune potentials
this.nullAssignmentValueBit1 =
~(~this.nullAssignmentStatusBit1 &
~this.nullAssignmentStatusBit2 &
~this.nullAssignmentValueBit1) &
~(this.nullAssignmentStatusBit1 &
(this.nullAssignmentStatusBit2 | this.nullAssignmentValueBit2));
// reset assignment and protected
this.nullAssignmentStatusBit1 =
this.nullAssignmentStatusBit2 = 0;
if (coverageTestFlag && coverageTestId == 38) {
this.nullAssignmentValueBit2 = ~0;
}
}
thisHasNulls = this.nullAssignmentStatusBit1 != 0 ||
this.nullAssignmentStatusBit2 != 0 ||
this.nullAssignmentValueBit1 != 0 ||
this.nullAssignmentValueBit2 != 0;
// treating extra storage
if (this.extra != null || otherInits.extra != null) {
int mergeLimit = 0, copyLimit = 0, resetLimit = 0;
if (this.extra != null) {
if (otherInits.extra != null) {
// both sides have extra storage
int length, otherLength;
if ((length = this.extra[0].length) <
(otherLength = otherInits.extra[0].length)) {
// current storage is shorter -> grow current
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], 0,
(this.extra[j] = new long[otherLength]), 0, length);
}
mergeLimit = length;
copyLimit = otherLength;
if (coverageTestFlag && coverageTestId == 39) {
throw new AssertionFailedException("COVERAGE 39"); //$NON-NLS-1$
}
}
else {
// current storage is longer
mergeLimit = otherLength;
resetLimit = length;
if (coverageTestFlag && coverageTestId == 40) {
throw new AssertionFailedException("COVERAGE 40"); //$NON-NLS-1$
}
}
}
else {
resetLimit = this.extra[0].length;
if (coverageTestFlag && coverageTestId == 41) {
throw new AssertionFailedException("COVERAGE 41"); //$NON-NLS-1$
}
}
}
else if (otherInits.extra != null) {
// no storage here, but other has extra storage.
int otherLength = otherInits.extra[0].length;
this.extra = new long[extraLength][];
for (int j = 0; j < extraLength; j++) {
this.extra[j] = new long[otherLength];
}
System.arraycopy(otherInits.extra[1], 0,
this.extra[1], 0, otherLength);
copyLimit = otherLength;
if (coverageTestFlag && coverageTestId == 42) {
throw new AssertionFailedException("COVERAGE 42"); //$NON-NLS-1$
}
}
int i;
if (otherHasNulls) {
for (i = 0; i < mergeLimit; i++) {
this.extra[2][i] =
(a1 = this.extra[2][i]) &
(b1 = otherInits.extra[2][i]) & (
(nb4 = ~(b4 = otherInits.extra[5][i])) &
((b2 = otherInits.extra[3][i]) &
(nb3 = ~(b3 = otherInits.extra[4][i])) &
(na3 = ~(a3 = this.extra[4][i])) &
((a2 = this.extra[3][i]) &
(na4 = ~(a4 = this.extra[5][i])) | a4) |
(na2 = ~a2) & a3 & na4 & (nb2 = ~b2) & b3 ) |
b4 & (na3 & nb3 & (na4 & a2 | a4) |
na2 & a4 & nb2));
this.extra[3][i] =
a2 & b2 & ~(a1 ^ b1) & (na3 & nb3 | na4 & nb4) |
a1 & b1 & (a2 ^ b2) & na3 & nb3 |
(a1 & na2 & (nb1 = ~b1) & b2 | ~a1 & a2 & b1 & nb2) & na4 & nb4;
this.extra[4][i] =
b1 & nb2 & nb4 |
~a1 & (a3 |
a2 & na3 & (b1 | nb2)) |
(a1 | na2) & nb1 & b2 & nb3 |
nb1 & b3 |
a1 & na2 & (na4 |
b1 & nb2 & (a3 | b3));
this.extra[5][i] =
a4 | b4;
thisHasNulls = thisHasNulls ||
this.extra[5][i] != 0 ||
this.extra[2][i] != 0 ||
this.extra[3][i] != 0 ||
this.extra[4][i] != 0;
if (coverageTestFlag && coverageTestId == 43) {
this.extra[5][i] = ~0;
}
}
}
else {
for (i = 0; i < mergeLimit; i++) {
this.extra[0][i] &=
otherInits.extra[0][i];
this.extra[1][i] |=
otherInits.extra[1][i];
this.extra[4][i] =
~(~this.extra[2][i] &
~this.extra[3][i] &
~this.extra[4][i]) &
~(this.extra[2][i] &
(this.extra[3][i] |
this.extra[5][i]));
this.extra[2][i] =
this.extra[3][i] = 0;
thisHasNulls = thisHasNulls ||
this.extra[4][i] != 0 ||
this.extra[5][i] != 0;
if (coverageTestFlag && coverageTestId == 44) {
this.extra[5][i] = ~0;
}
}
}
for (; i < copyLimit; i++) {
this.extra[1][i] = otherInits.extra[1][i];
this.extra[4][i] =
~(~otherInits.extra[2][i] &
~otherInits.extra[3][i] &
~otherInits.extra[4][i]) &
~(otherInits.extra[2][i] &
(otherInits.extra[3][i] |
otherInits.extra[5][i]));
this.extra[5][i] = otherInits.extra[5][i];
thisHasNulls = thisHasNulls ||
this.extra[4][i] != 0 ||
this.extra[5][i] != 0;
if (coverageTestFlag && coverageTestId == 45) {
this.extra[5][i] = ~0;
}
}
for (; i < resetLimit; i++) {
this.extra[4][i] =
~(~this.extra[2][i] &
~this.extra[3][i] &
~this.extra[4][i]) &
~(this.extra[2][i] &
(this.extra[3][i] |
this.extra[5][i]));
this.extra[0][i] =
this.extra[2][i] =
this.extra[3][i] = 0;
thisHasNulls = thisHasNulls ||
this.extra[4][i] != 0 ||
this.extra[5][i] != 0;
if (coverageTestFlag && coverageTestId == 46) {
this.extra[5][i] = ~0;
}
}
}
if (thisHasNulls) {
this.tagBits |= NULL_FLAG_MASK;
}
else {
this.tagBits &= ~NULL_FLAG_MASK;
}
return this;
}
/*
* Answer the total number of fields in enclosing types of a given type
*/
static int numberOfEnclosingFields(ReferenceBinding type){
int count = 0;
type = type.enclosingType();
while(type != null) {
count += type.fieldCount();
type = type.enclosingType();
}
return count;
}
public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() {
if (this == DEAD_END) {
return this;
}
UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
copy.definiteInits = this.definiteInits;
copy.potentialInits = this.potentialInits;
copy.tagBits = this.tagBits & ~NULL_FLAG_MASK;
copy.maxFieldCount = this.maxFieldCount;
if (this.extra != null) {
int length;
copy.extra = new long[extraLength][];
System.arraycopy(this.extra[0], 0,
(copy.extra[0] =
new long[length = this.extra[0].length]), 0, length);
System.arraycopy(this.extra[1], 0,
(copy.extra[1] = new long[length]), 0, length);
for (int j = 2; j < extraLength; j++) {
copy.extra[j] = new long[length];
}
}
return copy;
}
public FlowInfo safeInitsWhenTrue() {
return copy();
}
public FlowInfo setReachMode(int reachMode) {
if (reachMode == REACHABLE && this != DEAD_END) { // cannot modify DEAD_END
this.tagBits &= ~UNREACHABLE;
}
else {
if ((this.tagBits & UNREACHABLE) == 0) {
// reset optional inits when becoming unreachable
// see InitializationTest#test090 (and others)
this.potentialInits = 0;
if (this.extra != null) {
for (int i = 0, length = this.extra[0].length;
i < length; i++) {
this.extra[1][i] = 0;
}
}
}
this.tagBits |= UNREACHABLE;
}
return this;
}
public String toString(){
// PREMATURE consider printing bit fields as 0001 0001 1000 0001...
if (this == DEAD_END){
return "FlowInfo.DEAD_END"; //$NON-NLS-1$
}
if ((this.tagBits & NULL_FLAG_MASK) != 0) {
if (this.extra == null) {
return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$
+", pot: " + this.potentialInits //$NON-NLS-1$
+ ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
+", nullS1: " + this.nullAssignmentStatusBit1 //$NON-NLS-1$
+", nullS2: " + this.nullAssignmentStatusBit2 //$NON-NLS-1$
+", nullV1: " + this.nullAssignmentValueBit1 //$NON-NLS-1$
+", nullV2: " + this.nullAssignmentValueBit2 //$NON-NLS-1$
+">"; //$NON-NLS-1$
}
else {
String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$
pot = "], pot:[" + this.potentialInits, //$NON-NLS-1$
nullS1 = ", nullS1:[" + this.nullAssignmentStatusBit1, //$NON-NLS-1$
nullS2 = "], nullS2:[" + this.nullAssignmentStatusBit2, //$NON-NLS-1$
nullV1 = "], nullV1:[" + this.nullAssignmentValueBit1, //$NON-NLS-1$
nullV2 = "], nullV2:[" + this.nullAssignmentValueBit2; //$NON-NLS-1$
int i, ceil;
for (i = 0, ceil = this.extra[0].length > 3 ?
3 :
this.extra[0].length;
i < ceil; i++) {
def += "," + this.extra[0][i]; //$NON-NLS-1$
pot += "," + this.extra[1][i]; //$NON-NLS-1$
nullS1 += "," + this.extra[2][i]; //$NON-NLS-1$
nullS2 += "," + this.extra[3][i]; //$NON-NLS-1$
nullV1 += "," + this.extra[4][i]; //$NON-NLS-1$
nullV2 += "," + this.extra[5][i]; //$NON-NLS-1$
}
if (ceil < this.extra[0].length) {
def += ",..."; //$NON-NLS-1$
pot += ",..."; //$NON-NLS-1$
nullS1 += ",..."; //$NON-NLS-1$
nullS2 += ",..."; //$NON-NLS-1$
nullV1 += ",..."; //$NON-NLS-1$
nullV2 += ",..."; //$NON-NLS-1$
}
return def + pot
+ "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
+ nullS1 + nullS2 + nullV1 + nullV2
+ "]>"; //$NON-NLS-1$
}
}
else {
if (this.extra == null) {
return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$
+", pot: " + this.potentialInits //$NON-NLS-1$
+ ", reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
+", no null info>"; //$NON-NLS-1$
}
else {
String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$
pot = "], pot:[" + this.potentialInits; //$NON-NLS-1$
int i, ceil;
for (i = 0, ceil = this.extra[0].length > 3 ?
3 :
this.extra[0].length;
i < ceil; i++) {
def += "," + this.extra[0][i]; //$NON-NLS-1$
pot += "," + this.extra[1][i]; //$NON-NLS-1$
}
if (ceil < this.extra[0].length) {
def += ",..."; //$NON-NLS-1$
pot += ",..."; //$NON-NLS-1$
}
return def + pot
+ "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
+ ", no null info>"; //$NON-NLS-1$
}
}
}
public UnconditionalFlowInfo unconditionalCopy() {
return (UnconditionalFlowInfo) copy();
}
public UnconditionalFlowInfo unconditionalFieldLessCopy() {
// TODO (maxime) may consider leveraging null contribution verification as it is done in copy
UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
copy.tagBits = this.tagBits;
copy.maxFieldCount = this.maxFieldCount;
int limit = this.maxFieldCount;
if (limit < BitCacheSize) {
long mask;
copy.definiteInits = this.definiteInits & (mask = ~((1L << limit)-1));
copy.potentialInits = this.potentialInits & mask;
copy.nullAssignmentStatusBit1 = this.nullAssignmentStatusBit1 & mask;
copy.nullAssignmentStatusBit2 = this.nullAssignmentStatusBit2 & mask;
copy.nullAssignmentValueBit1 = this.nullAssignmentValueBit1 & mask;
copy.nullAssignmentValueBit2 = this.nullAssignmentValueBit2 & mask;
}
// use extra vector
if (this.extra == null) {
return copy; // if vector not yet allocated, then not initialized
}
int vectorIndex, length, copyStart;
if ((vectorIndex = (limit / BitCacheSize) - 1) >=
(length = this.extra[0].length)) {
return copy; // not enough room yet
}
long mask;
copy.extra = new long[extraLength][];
if ((copyStart = vectorIndex + 1) < length) {
int copyLength = length - copyStart;
for (int j = 0; j < extraLength; j++) {
System.arraycopy(this.extra[j], copyStart,
(copy.extra[j] = new long[length]), copyStart,
copyLength);
}
}
else if (vectorIndex >= 0) {
for (int j = 0; j < extraLength; j++) {
copy.extra[j] = new long[length];
}
}
if (vectorIndex >= 0) {
mask = ~((1L << (limit % BitCacheSize))-1);
for (int j = 0; j < extraLength; j++) {
copy.extra[j][vectorIndex] =
this.extra[j][vectorIndex] & mask;
}
}
return copy;
}
public UnconditionalFlowInfo unconditionalInits() {
// also see conditional inits, where it requests them to merge
return this;
}
public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() {
return this;
}
}