*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
-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;
public long extraDefiniteInits[];
public long extraPotentialInits[];
+ public long definiteNulls;
+ public long definiteNonNulls;
+ public long extraDefiniteNulls[];
+ public long extraDefiniteNonNulls[];
+
public int reachMode; // by default
public int maxFieldCount;
// union of potentially set ones
potentialInits |= otherInits.potentialInits;
+ // union of definitely null variables,
+ definiteNulls = (definiteNulls | otherInits.definiteNulls) & ~otherInits.definiteNonNulls;
+ // union of definitely non null variables,
+ definiteNonNulls = (definiteNonNulls | otherInits.definiteNonNulls) & ~otherInits.definiteNulls;
+ // fix-up null/non-null infos since cannot overlap: <defN1:0,defNoN1:1> + <defN2:1,defNoN2:0> --> <defN:0,defNon:0>
+
// treating extra storage
if (extraDefiniteInits != null) {
if (otherInits.extraDefiniteInits != null) {
// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
- while (i < length) {
+ for (; i < length; i++) {
extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
- extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i];
+ extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i];
}
- while (i < otherLength) {
- extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
+ for (; i < otherLength; i++) {
+ extraPotentialInits[i] = otherInits.extraPotentialInits[i];
}
} else {
// current storage is longer
- while (i < otherLength) {
+ for (; i < otherLength; i++) {
extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
- extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ extraDefiniteNulls[i] = (extraDefiniteNulls[i] | otherInits.extraDefiniteNulls[i]) & ~otherInits.extraDefiniteNonNulls[i];
+ extraDefiniteNonNulls[i] = (extraDefiniteNonNulls[i] | otherInits.extraDefiniteNonNulls[i]) & ~otherInits.extraDefiniteNulls[i];
+ }
+ for (; i < length; i++) {
+ extraDefiniteInits[i] = 0;
+ extraDefiniteNulls[i] = 0;
+ extraDefiniteNonNulls[i] = 0;
}
- while (i < length)
- extraDefiniteInits[i++] = 0;
}
} else {
// no extra storage on otherInits
int otherLength;
System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength);
System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
+ System.arraycopy(otherInits.extraDefiniteNulls, 0, (extraDefiniteNulls = new long[otherLength]), 0, otherLength);
+ System.arraycopy(otherInits.extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[otherLength]), 0, otherLength);
}
return this;
}
return this;
}
// union of potentially set ones
- potentialInits |= otherInits.potentialInits;
+ this.potentialInits |= otherInits.potentialInits;
+ // also merge null check information (affected by potential inits)
+ this.definiteNulls &= otherInits.definiteNulls;
+ this.definiteNonNulls &= otherInits.definiteNonNulls;
// treating extra storage
- if (extraDefiniteInits != null) {
+ if (this.extraDefiniteInits != null) {
if (otherInits.extraDefiniteInits != null) {
// both sides have extra storage
int i = 0, length, otherLength;
- if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
+ if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
- System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
- System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length);
while (i < length) {
- extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
+ this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
}
while (i < otherLength) {
- extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
+ this.extraPotentialInits[i] = otherInits.extraPotentialInits[i];
+ this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
+ this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
}
} else {
// current storage is longer
while (i < otherLength) {
- extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
+ this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
}
}
}
if (otherInits.extraDefiniteInits != null) {
// no storage here, but other has extra storage.
int otherLength;
- extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
- System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
+ this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
+ System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
+ this.extraDefiniteNulls = new long[otherLength];
+ this.extraDefiniteNonNulls = new long[otherLength];
}
return this;
}
// copy slots
copy.definiteInits = this.definiteInits;
copy.potentialInits = this.potentialInits;
+ copy.definiteNulls = this.definiteNulls;
+ copy.definiteNonNulls = this.definiteNonNulls;
copy.reachMode = this.reachMode;
copy.maxFieldCount = this.maxFieldCount;
if (this.extraDefiniteInits != null) {
int length;
- System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[ (length = extraDefiniteInits.length)]), 0, length);
+ System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[length = extraDefiniteInits.length]), 0, length);
System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length);
+ System.arraycopy(this.extraDefiniteNulls, 0, (copy.extraDefiniteNulls = new long[length]), 0, length);
+ System.arraycopy(this.extraDefiniteNonNulls, 0, (copy.extraDefiniteNonNulls = new long[length]), 0, length);
}
return copy;
}
long mask = (1L << limit)-1;
this.definiteInits &= ~mask;
this.potentialInits &= ~mask;
+ this.definiteNulls &= ~mask;
+ this.definiteNonNulls &= ~mask;
return this;
}
this.definiteInits = 0;
this.potentialInits = 0;
-
+ this.definiteNulls = 0;
+ this.definiteNonNulls = 0;
+
// use extra vector
if (extraDefiniteInits == null) {
return this; // if vector not yet allocated, then not initialized
for (int i = 0; i < vectorIndex; i++) {
this.extraDefiniteInits[i] = 0L;
this.extraPotentialInits[i] = 0L;
+ this.extraDefiniteNulls[i] = 0L;
+ this.extraDefiniteNonNulls[i] = 0L;
}
long mask = (1L << (limit % BitCacheSize))-1;
this.extraDefiniteInits[vectorIndex] &= ~mask;
this.extraPotentialInits[vectorIndex] &= ~mask;
+ this.extraDefiniteNulls[vectorIndex] &= ~mask;
+ this.extraDefiniteNonNulls[vectorIndex] &= ~mask;
return this;
}
long mask = (1L << limit)-1;
this.definiteInits &= mask;
this.potentialInits &= mask;
+ this.definiteNulls &= mask;
+ this.definiteNonNulls &= mask;
return this;
}
// use extra vector
long mask = (1L << (limit % BitCacheSize))-1;
this.extraDefiniteInits[vectorIndex] &= mask;
this.extraPotentialInits[vectorIndex] &= mask;
+ this.extraDefiniteNulls[vectorIndex] &= mask;
+ this.extraDefiniteNonNulls[vectorIndex] &= mask;
for (int i = vectorIndex+1; i < length; i++) {
this.extraDefiniteInits[i] = 0L;
this.extraPotentialInits[i] = 0L;
+ this.extraDefiniteNulls[i] = 0L;
+ this.extraDefiniteNonNulls[i] = 0L;
}
return this;
}
+ public UnconditionalFlowInfo discardNullRelatedInitializations(){
+
+ this.definiteNulls = 0;
+ this.definiteNonNulls = 0;
+
+ int length = this.extraDefiniteInits == null ? 0 : this.extraDefiniteInits.length;
+ for (int i = 0; i < length; i++) {
+ this.extraDefiniteNulls[i] = 0L;
+ this.extraDefiniteNonNulls[i] = 0L;
+ }
+ return this;
+ }
+
public FlowInfo initsWhenFalse() {
return this;
}
/**
+ * Check status of definite non-null assignment at a given position.
+ * It deals with the dual representation of the InitializationInfo2:
+ * bits for the first 64 entries, then an array of booleans.
+ */
+ final private boolean isDefinitelyNonNull(int position) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // id is zero-based
+ if (position < BitCacheSize) {
+ return (definiteNonNulls & (1L << position)) != 0; // use bits
+ }
+ // use extra vector
+ if (extraDefiniteNonNulls == null)
+ return false; // if vector not yet allocated, then not initialized
+ int vectorIndex;
+ if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNonNulls.length)
+ return false; // if not enough room in vector, then not initialized
+ return ((extraDefiniteNonNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
+ }
+
+ /**
+ * Check status of definite null assignment at a given position.
+ * It deals with the dual representation of the InitializationInfo2:
+ * bits for the first 64 entries, then an array of booleans.
+ */
+ final private boolean isDefinitelyNull(int position) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // id is zero-based
+ if (position < BitCacheSize) {
+ return (definiteNulls & (1L << position)) != 0; // use bits
+ }
+ // use extra vector
+ if (extraDefiniteNulls == null)
+ return false; // if vector not yet allocated, then not initialized
+ int vectorIndex;
+ if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteNulls.length)
+ return false; // if not enough room in vector, then not initialized
+ return ((extraDefiniteNulls[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
+ }
+
+ /**
* Check status of definite assignment for a field.
*/
final public boolean isDefinitelyAssigned(FieldBinding field) {
// We do not want to complain in unreachable code
if ((this.reachMode & UNREACHABLE) != 0)
return true;
- if (local.isArgument) {
+
+ // final constants are inlined, and thus considered as always initialized
+ if (local.isConstantValue()) {
return true;
}
+ return isDefinitelyAssigned(local.id + maxFieldCount);
+ }
+
+ /**
+ * Check status of definite non-null assignment for a field.
+ */
+ final public boolean isDefinitelyNonNull(FieldBinding field) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // We do not want to complain in unreachable code
+ if ((this.reachMode & UNREACHABLE) != 0)
+ return false;
+ return isDefinitelyNonNull(field.id);
+ }
+
+ /**
+ * Check status of definite non-null assignment for a local.
+ */
+ final public boolean isDefinitelyNonNull(LocalVariableBinding local) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // We do not want to complain in unreachable code
+ if ((this.reachMode & UNREACHABLE) != 0)
+ return false;
// final constants are inlined, and thus considered as always initialized
- if (local.constant != Constant.NotAConstant) {
+ if (local.isConstantValue()) {
return true;
}
- return isDefinitelyAssigned(local.id + maxFieldCount);
+ return isDefinitelyNonNull(local.id + maxFieldCount);
+ }
+
+ /**
+ * Check status of definite null assignment for a field.
+ */
+ final public boolean isDefinitelyNull(FieldBinding field) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // We do not want to complain in unreachable code
+ if ((this.reachMode & UNREACHABLE) != 0)
+ return false;
+ return isDefinitelyNull(field.id);
}
+ /**
+ * Check status of definite null assignment for a local.
+ */
+ final public boolean isDefinitelyNull(LocalVariableBinding local) {
+
+ // Dependant of CodeStream.isDefinitelyAssigned(..)
+ // We do not want to complain in unreachable code
+ if ((this.reachMode & UNREACHABLE) != 0)
+ return false;
+ return isDefinitelyNull(local.id + maxFieldCount);
+ }
+
public boolean isReachable() {
return this.reachMode == REACHABLE;
*/
final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
- if (local.isArgument) {
- return true;
- }
// final constants are inlined, and thus considered as always initialized
- if (local.constant != Constant.NotAConstant) {
+ if (local.isConstantValue()) {
return true;
}
return isPotentiallyAssigned(local.id + maxFieldCount);
long mask;
definiteInits |= (mask = 1L << position);
potentialInits |= mask;
+ definiteNulls &= ~mask;
+ definiteNonNulls &= ~mask;
} else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
int length;
extraDefiniteInits = new long[length = vectorIndex + 1];
extraPotentialInits = new long[length];
+ extraDefiniteNulls = new long[length];
+ extraDefiniteNonNulls = new long[length];
} else {
int oldLength; // might need to grow the arrays
if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength);
System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength);
+ System.arraycopy(extraDefiniteNulls, 0, (extraDefiniteNulls = new long[vectorIndex + 1]), 0, oldLength);
+ System.arraycopy(extraDefiniteNonNulls, 0, (extraDefiniteNonNulls = new long[vectorIndex + 1]), 0, oldLength);
}
}
long mask;
extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
extraPotentialInits[vectorIndex] |= mask;
+ extraDefiniteNulls[vectorIndex] &= ~mask;
+ extraDefiniteNonNulls[vectorIndex] &= ~mask;
}
}
}
if (this != DEAD_END)
markAsDefinitelyAssigned(local.id + maxFieldCount);
}
+
+ /**
+ * Record a definite non-null assignment at a given position.
+ * It deals with the dual representation of the InitializationInfo2:
+ * bits for the first 64 entries, then an array of booleans.
+ */
+ final private void markAsDefinitelyNonNull(int position) {
+
+ if (this != DEAD_END) {
+
+ // position is zero-based
+ if (position < BitCacheSize) {
+ // use bits
+ long mask;
+ definiteNonNulls |= (mask = 1L << position);
+ definiteNulls &= ~mask;
+ } else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ long mask;
+ extraDefiniteNonNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
+ extraDefiniteNulls[vectorIndex] &= ~mask;
+ }
+ }
+ }
+
+ /**
+ * Record a field got definitely assigned to non-null value.
+ */
+ public void markAsDefinitelyNonNull(FieldBinding field) {
+ if (this != DEAD_END)
+ markAsDefinitelyNonNull(field.id);
+ }
+
+ /**
+ * Record a local got definitely assigned to non-null value.
+ */
+ public void markAsDefinitelyNonNull(LocalVariableBinding local) {
+ if (this != DEAD_END)
+ markAsDefinitelyNonNull(local.id + maxFieldCount);
+ }
+
+ /**
+ * Record a definite null assignment at a given position.
+ * It deals with the dual representation of the InitializationInfo2:
+ * bits for the first 64 entries, then an array of booleans.
+ */
+ final private void markAsDefinitelyNull(int position) {
+
+ if (this != DEAD_END) {
+
+ // position is zero-based
+ if (position < BitCacheSize) {
+ // use bits
+ long mask;
+ definiteNulls |= (mask = 1L << position);
+ definiteNonNulls &= ~mask;
+ } else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ long mask;
+ extraDefiniteNulls[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
+ extraDefiniteNonNulls[vectorIndex] &= ~mask;
+ }
+ }
+ }
+
+ /**
+ * Record a field got definitely assigned to null.
+ */
+ public void markAsDefinitelyNull(FieldBinding field) {
+ if (this != DEAD_END)
+ markAsDefinitelyAssigned(field.id);
+ }
+
+ /**
+ * Record a local got definitely assigned to null.
+ */
+ public void markAsDefinitelyNull(LocalVariableBinding local) {
+ if (this != DEAD_END)
+ markAsDefinitelyNull(local.id + maxFieldCount);
+ }
/**
* Clear initialization information at a given position.
long mask;
definiteInits &= ~(mask = 1L << position);
potentialInits &= ~mask;
+ definiteNulls &= ~mask;
+ definiteNonNulls &= ~mask;
} else {
// use extra vector
int vectorIndex = (position / BitCacheSize) - 1;
long mask;
extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
extraPotentialInits[vectorIndex] &= ~mask;
+ extraDefiniteNulls[vectorIndex] &= ~mask;
+ extraDefiniteNonNulls[vectorIndex] &= ~mask;
}
}
}
this.definiteInits &= otherInits.definiteInits;
// union of potentially set ones
this.potentialInits |= otherInits.potentialInits;
+ // intersection of definitely null variables,
+ this.definiteNulls &= otherInits.definiteNulls;
+ // intersection of definitely non-null variables,
+ this.definiteNonNulls &= otherInits.definiteNonNulls;
// treating extra storage
if (this.extraDefiniteInits != null) {
// current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraDefiniteNulls, 0, (this.extraDefiniteNulls = new long[otherLength]), 0, length);
+ System.arraycopy(this.extraDefiniteNonNulls, 0, (this.extraDefiniteNonNulls = new long[otherLength]), 0, length);
while (i < length) {
this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
+ this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
}
while (i < otherLength) {
this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
// current storage is longer
while (i < otherLength) {
this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
- this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
+ this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i];
+ this.extraDefiniteNulls[i] &= otherInits.extraDefiniteNulls[i];
+ this.extraDefiniteNonNulls[i] &= otherInits.extraDefiniteNonNulls[i++];
+ }
+ while (i < length) {
+ this.extraDefiniteInits[i] = 0;
+ this.extraDefiniteNulls[i] = 0;
+ this.extraDefiniteNonNulls[i++] = 0;
}
- while (i < length)
- this.extraDefiniteInits[i++] = 0;
}
} else {
// no extra storage on otherInits
int i = 0, length = this.extraDefiniteInits.length;
- while (i < length)
- this.extraDefiniteInits[i++] = 0;
+ while (i < length) {
+ this.extraDefiniteInits[i] = 0;
+ this.extraDefiniteNulls[i] = 0;
+ this.extraDefiniteNonNulls[i++] = 0;
+ }
}
} else
if (otherInits.extraDefiniteInits != null) {
int otherLength;
this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
+ this.extraDefiniteNulls = new long[otherLength];
+ this.extraDefiniteNonNulls = new long[otherLength];
}
return this;
}
return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$
+", pot: " + this.potentialInits //$NON-NLS-1$
+ ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
+ +", defNull: " + this.definiteNulls //$NON-NLS-1$
+ +", defNonNull: " + this.definiteNonNulls //$NON-NLS-1$
+">"; //$NON-NLS-1$
}