|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.tree; |
|
|
|
import sun.tools.java.*; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final |
|
class Vset implements Constants { |
|
long vset; |
|
long uset; |
|
|
|
// The extension array is interleaved, consisting of alternating |
|
// blocks of 64 DA bits followed by 64 DU bits followed by 64 DA |
|
// bits, and so on. |
|
|
|
long x[]; |
|
|
|
// An infinite vector of zeroes or an infinite vector of ones is |
|
// represented by a special value of the extension array. |
|
// |
|
// IMPORTANT: The condition 'this.x == fullX' is used as a marker for |
|
// unreachable code, i.e., for a dead-end. We maintain the invariant |
|
// that (this.x != fullX || (this.vset == -1 && this.uset == -1)). |
|
// A dead-end has the peculiar property that all variables are both |
|
// definitely assigned and definitely unassigned. We always force this |
|
// condition to hold, even when the normal bitvector operations performed |
|
// during DA/DU analysis would produce a different result. This supresses |
|
// reporting of DA/DU errors in unreachable code. |
|
|
|
static final long emptyX[] = new long[0]; |
|
static final long fullX[] = new long[0]; |
|
|
|
// For more thorough testing of long vset support, it is helpful to |
|
// temporarily redefine this value to a smaller number, such as 1 or 2. |
|
|
|
static final int VBITS = 64; |
|
|
|
/** |
|
* This is the Vset which reports all vars assigned and unassigned. |
|
* This impossibility is degenerately true exactly when |
|
* control flow cannot reach this point. |
|
*/ |
|
|
|
// We distinguish a canonical dead-end value generated initially for |
|
// statements that do not complete normally, making the next one unreachable. |
|
// Once an unreachable statement is reported, a non-canonical dead-end value |
|
// is used for subsequent statements in order to suppress redundant error |
|
// messages. |
|
|
|
static final Vset DEAD_END = new Vset(-1, -1, fullX); |
|
|
|
|
|
|
|
*/ |
|
public Vset() { |
|
this.x = emptyX; |
|
} |
|
|
|
private Vset(long vset, long uset, long x[]) { |
|
this.vset = vset; |
|
this.uset = uset; |
|
this.x = x; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset copy() { |
|
if (this == DEAD_END) { |
|
return this; |
|
} |
|
Vset vs = new Vset(vset, uset, x); |
|
if (x.length > 0) { |
|
vs.growX(x.length); |
|
} |
|
return vs; |
|
} |
|
|
|
private void growX(int length) { |
|
long newX[] = new long[length]; |
|
long oldX[] = x; |
|
for (int i = 0; i < oldX.length; i++) { |
|
newX[i] = oldX[i]; |
|
} |
|
x = newX; |
|
} |
|
|
|
/** |
|
* Ask if this is a vset for a dead end. |
|
* Answer true only for the canonical dead-end, DEAD_END. |
|
* A canonical dead-end is produced only as a result of |
|
* a statement that cannot complete normally, as specified |
|
* by the JLS. Due to the special-case rules for if-then |
|
* and if-then-else, this may fail to detect actual unreachable |
|
* code that could easily be identified. |
|
*/ |
|
|
|
public boolean isDeadEnd() { |
|
return (this == DEAD_END); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isReallyDeadEnd() { |
|
return (x == fullX); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset clearDeadEnd() { |
|
if (this == DEAD_END) { |
|
return new Vset(-1, -1, fullX); |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean testVar(int varNumber) { |
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
int i = (varNumber / VBITS - 1) * 2; |
|
if (i >= x.length) { |
|
return (x == fullX); |
|
} |
|
return (x[i] & bit) != 0; |
|
} else { |
|
return (vset & bit) != 0; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean testVarUnassigned(int varNumber) { |
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
|
|
int i = ((varNumber / VBITS - 1) * 2) + 1; |
|
if (i >= x.length) { |
|
return (x == fullX); |
|
} |
|
return (x[i] & bit) != 0; |
|
} else { |
|
return (uset & bit) != 0; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset addVar(int varNumber) { |
|
if (x == fullX) { |
|
return this; |
|
} |
|
|
|
// gen DA, kill DU |
|
|
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
int i = (varNumber / VBITS - 1) * 2; |
|
if (i >= x.length) { |
|
growX(i+1); |
|
} |
|
x[i] |= bit; |
|
if (i+1 < x.length) { |
|
x[i+1] &=~ bit; |
|
} |
|
} else { |
|
vset |= bit; |
|
uset &=~ bit; |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset addVarUnassigned(int varNumber) { |
|
if (x == fullX) { |
|
return this; |
|
} |
|
|
|
// gen DU, kill DA |
|
|
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
|
|
int i = ((varNumber / VBITS - 1) * 2) + 1; |
|
if (i >= x.length) { |
|
growX(i+1); |
|
} |
|
x[i] |= bit; |
|
x[i-1] &=~ bit; |
|
} else { |
|
uset |= bit; |
|
vset &=~ bit; |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset clearVar(int varNumber) { |
|
if (x == fullX) { |
|
return this; |
|
} |
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
int i = (varNumber / VBITS - 1) * 2; |
|
if (i >= x.length) { |
|
return this; |
|
} |
|
x[i] &=~ bit; |
|
if (i+1 < x.length) { |
|
x[i+1] &=~ bit; |
|
} |
|
} else { |
|
vset &=~ bit; |
|
uset &=~ bit; |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset join(Vset other) { |
|
|
|
// Return a dead-end if both vsets are dead-ends. |
|
// Return the canonical DEAD_END only if both vsets |
|
// are the canonical DEAD_END. Otherwise, an incoming |
|
// dead-end vset has already produced an error message, |
|
|
|
if (this == DEAD_END) { |
|
return other.copy(); |
|
} |
|
if (other == DEAD_END) { |
|
return this; |
|
} |
|
if (x == fullX) { |
|
return other.copy(); |
|
} |
|
if (other.x == fullX) { |
|
return this; |
|
} |
|
|
|
// DA = DA intersection DA |
|
// DU = DU intersection DU |
|
|
|
vset &= other.vset; |
|
uset &= other.uset; |
|
|
|
if (other.x == emptyX) { |
|
x = emptyX; |
|
} else { |
|
|
|
long otherX[] = other.x; |
|
int selfLength = x.length; |
|
int limit = (otherX.length < selfLength) ? otherX.length : selfLength; |
|
for (int i = 0; i < limit; i++) { |
|
x[i] &= otherX[i]; |
|
} |
|
// If self is longer than other, all remaining |
|
// bits are implicitly 0. In the result, then, |
|
|
|
for (int i = limit; i < selfLength; i++) { |
|
x[i] = 0; |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
/** |
|
* Add in the definite assignment bits of another vset, |
|
* but join the definite unassignment bits. This unusual |
|
* operation is used only for 'finally' blocks. The |
|
* original vset 'this' is destroyed by this operation. |
|
* (Part of fix for 4068688.) |
|
*/ |
|
|
|
public Vset addDAandJoinDU(Vset other) { |
|
|
|
// Return a dead-end if either vset is a dead end. |
|
// If either vset is the canonical DEAD_END, the |
|
|
|
if (this == DEAD_END) { |
|
return this; |
|
} |
|
if (other == DEAD_END) { |
|
return other; |
|
} |
|
if (x == fullX) { |
|
return this; |
|
} |
|
if (other.x == fullX) { |
|
return other.copy(); |
|
} |
|
|
|
// DA = DA union DA' |
|
// DU = (DU intersection DU') - DA' |
|
|
|
vset = vset | other.vset; |
|
uset = (uset & other.uset) & ~other.vset; |
|
|
|
int selfLength = x.length; |
|
long otherX[] = other.x; |
|
int otherLength = otherX.length; |
|
|
|
if (otherX != emptyX) { |
|
|
|
if (otherLength > selfLength) { |
|
growX(otherLength); |
|
} |
|
int i = 0; |
|
while (i < otherLength) { |
|
x[i] |= otherX[i]; |
|
i++; |
|
if (i == otherLength) break; |
|
x[i] = ((x[i] & otherX[i]) & ~otherX[i-1]); |
|
i++; |
|
} |
|
} |
|
// If self is longer than other, all remaining |
|
// bits are implicitly 0. In the result, then, |
|
// the remaining DA bits are left unchanged, and |
|
// the DU bits are all cleared. First, align |
|
|
|
for (int i = (otherLength | 1); i < selfLength; i += 2) { |
|
x[i] = 0; |
|
} |
|
return this; |
|
} |
|
|
|
|
|
/** |
|
* Construct a vset consisting of the DA bits of the first argument |
|
* and the DU bits of the second argument. This is a higly unusual |
|
* operation, as it implies a case where the flowgraph for DA analysis |
|
* differs from that for DU analysis. It is only needed for analysing |
|
* 'try' blocks. The result is a dead-end iff the first argument is |
|
* dead-end. (Part of fix for 4068688.) |
|
*/ |
|
|
|
public static Vset firstDAandSecondDU(Vset sourceDA, Vset sourceDU) { |
|
|
|
// Note that reachability status is received via 'sourceDA' only! |
|
// This is a consequence of the fact that reachability and DA |
|
// analysis are performed on an identical flow graph, whereas the |
|
|
|
if (sourceDA.x == fullX) { |
|
return sourceDA.copy(); |
|
} |
|
|
|
long sourceDAx[] = sourceDA.x; |
|
int lenDA = sourceDAx.length; |
|
long sourceDUx[] = sourceDU.x; |
|
int lenDU = sourceDUx.length; |
|
int limit = (lenDA > lenDU) ? lenDA : lenDU; |
|
long x[] = emptyX; |
|
|
|
if (limit > 0) { |
|
x = new long[limit]; |
|
for (int i = 0; i < lenDA; i += 2) { |
|
x[i] = sourceDAx[i]; |
|
} |
|
for (int i = 1; i < lenDU; i += 2) { |
|
x[i] = sourceDUx[i]; |
|
} |
|
} |
|
|
|
return new Vset(sourceDA.vset, sourceDU.uset, x); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Vset removeAdditionalVars(int varNumber) { |
|
if (x == fullX) { |
|
return this; |
|
} |
|
long bit = (1L << varNumber); |
|
if (varNumber >= VBITS) { |
|
int i = (varNumber / VBITS - 1) * 2; |
|
if (i < x.length) { |
|
x[i] &= (bit - 1); |
|
if (++i < x.length) { |
|
x[i] &= (bit - 1); |
|
} |
|
while (++i < x.length) { |
|
x[i] = 0; |
|
} |
|
} |
|
} else { |
|
if (x.length > 0) { |
|
x = emptyX; |
|
} |
|
vset &= (bit - 1); |
|
uset &= (bit - 1); |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int varLimit() { |
|
long vset; |
|
int result; |
|
scan: { |
|
for (int i = (x.length / 2) * 2; i >= 0; i -= 2) { |
|
if (i == x.length) continue; |
|
vset = x[i]; |
|
if (i+1 < x.length) { |
|
vset |= x[i+1]; |
|
} |
|
if (vset != 0) { |
|
result = (i/2 + 1) * VBITS; |
|
break scan; |
|
} |
|
} |
|
vset = this.vset; |
|
vset |= this.uset; |
|
if (vset != 0) { |
|
result = 0; |
|
break scan; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
while (vset != 0) { |
|
result += 1; |
|
vset >>>= 1; |
|
} |
|
return result; |
|
} |
|
|
|
public String toString() { |
|
if (this == DEAD_END) |
|
return "{DEAD_END}"; |
|
StringBuffer sb = new StringBuffer("{"); |
|
int maxVar = VBITS * (1 + (x.length+1)/2); |
|
for (int i = 0; i < maxVar; i++) { |
|
if (!testVarUnassigned(i)) { |
|
if (sb.length() > 1) { |
|
sb.append(' '); |
|
} |
|
sb.append(i); |
|
if (!testVar(i)) { |
|
sb.append('?'); |
|
} |
|
} |
|
} |
|
if (x == fullX) { |
|
sb.append("...DEAD_END"); |
|
} |
|
sb.append('}'); |
|
return sb.toString(); |
|
} |
|
|
|
} |