Logo Search packages:      
Sourcecode: ecj version File versions

UnconditionalFlowInfo.java

/*******************************************************************************
 * Copyright (c) 2000, 2006 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.compiler.ast.ASTNode;
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.
 */
00025 public class UnconditionalFlowInfo extends FlowInfo {
      // Coverage tests
      /**
       * Exception raised when unexpected behavior is detected during coverage
       * tests. 
       */
00031       public static class AssertionFailedException extends RuntimeException {
            private static final long serialVersionUID = 1827352841030089703L;
            
      public AssertionFailedException(String message) {
            super(message);
      }
      }
      
      // 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;

      // assignment bits - first segment
      public long definiteInits;
      public long potentialInits;
      
      // null bits - first segment
      public long 
            nullBit1,
            nullBit2,
            nullBit3,
            nullBit4;
/*
            nullBit1
             nullBit2...
            0000  start
            0001  pot. unknown
            0010  pot. non null
            0011  pot. nn & pot. un
            0100  pot. null
            0101  pot. n & pot. un
            0110  pot. n & pot. nn
            1001  def. unknown
            1010  def. non null
            1011  pot. nn & prot. nn
            1100  def. null
            1101  pot. n & prot. n
            1110  prot. null
            1111  prot. non null
 */   
            
      // extra segments
      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
      boolean thisHadNulls = (this.tagBits & NULL_FLAG_MASK) != 0,
            otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0;
      long 
            a1, a2, a3, a4, 
            na1, na2, na3, na4, 
            b1, b2, b3, b4,
            nb1, nb2, nb3, nb4;
      if (otherHasNulls) {
            if (!thisHadNulls) {
                  this.nullBit1 = otherInits.nullBit1;
                  this.nullBit2 = otherInits.nullBit2;
                  this.nullBit3 = otherInits.nullBit3;
                  this.nullBit4 = otherInits.nullBit4;
                  if (coverageTestFlag && coverageTestId == 1) {
                    this.nullBit4 = ~0;
                  }
            }
            else {
                  this.nullBit1 = (b1 = otherInits.nullBit1)
                                    | (a1 = this.nullBit1) & ((a3 = this.nullBit3) 
                                          & (a4 = this.nullBit4) & (nb2 = ~(b2 = otherInits.nullBit2)) 
                                          & (nb4 = ~(b4 = otherInits.nullBit4))
                                    | ((na4 = ~a4) | (na3 = ~a3)) 
                                          & ((na2 = ~(a2 = this.nullBit2)) & nb2 
                                                | a2 & (nb3 = ~(b3 = otherInits.nullBit3)) & nb4));
                  this.nullBit2  = b2 & (nb4 | nb3)
                                    | na3 & na4 & b2
                                    | a2 & (nb3 & nb4
                                                | (nb1 = ~b1) & (na3 | (na1 = ~a1))
                                                | a1 & b2);
                  this.nullBit3 = b3 & (nb1 & (b2 | a2 | na1)
                                          | b1 & (b4 | nb2 | a1 & a3)
                                          | na1 & na2 & na4)
                                    | a3 & nb2 & nb4
                                    | nb1 & ((na2 & a4 | na1) & a3
                                                | a1 & na2 & na4 & b2);
                  this.nullBit4 = nb1 & (a4 & (na3 & nb3    | (a3 | na2) & nb2)
                                    | a1 & (a3 & nb2 & b4
                                                | a2 & b2 & (b4   | a3 & na4 & nb3)))
                                    | b1 & (a3 & a4 & b4
                                          | na2 & na4 & nb3 & b4
                                          | a2 & ((b3 | a4) & b4
                                                      | na3 & a4 & b2 & b3)
                                          | na1 & (b4 | (a4 | a2) & b2 & b3))
                                    | (na1 & (na3 & nb3 | na2 & nb2)
                                          | a1 & (nb2 & nb3 | a2 & a3)) & b4; 
                  if (coverageTestFlag && coverageTestId == 2) {
                    this.nullBit4 = ~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)) {
                              // 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 == 3) {
                                    throw new AssertionFailedException("COVERAGE 3"); //$NON-NLS-1$
                              }
                        } else {
                              // current storage is longer
                              mergeLimit = otherLength;
                              if (coverageTestFlag && coverageTestId == 4) {
                                    throw new AssertionFailedException("COVERAGE 4"); //$NON-NLS-1$
                              }
                        }
                  } 
            } 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 (otherHasNulls) {
                        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) {
                              throw new AssertionFailedException("COVERAGE 6"); //$NON-NLS-1$
                        }
                  }
            }
            int i;
            // manage definite assignment info
            for (i = 0; i < mergeLimit; i++) {
                  this.extra[0][i] |= otherInits.extra[0][i];
                  this.extra[1][i] |= otherInits.extra[1][i];
            }
            for (; i < copyLimit; i++) {
                  this.extra[0][i] = otherInits.extra[0][i];
                  this.extra[1][i] = otherInits.extra[1][i];
            }
            // tweak limits for nulls
            if (!thisHadNulls) {
                if (copyLimit < mergeLimit) {
                        copyLimit = mergeLimit;
                }
                  mergeLimit = 0;
            }
            if (!otherHasNulls) {
                  copyLimit = 0;
                  mergeLimit = 0;
            }
            for (i = 0; i < mergeLimit; i++) {
                  this.extra[1 + 1][i] = (b1 = otherInits.extra[1 + 1][i])
                                    | (a1 = this.extra[1 + 1][i]) & ((a3 = this.extra[3 + 1][i]) 
                                          & (a4 = this.extra[4 + 1][i]) & (nb2 = ~(b2 = otherInits.extra[2 + 1][i])) 
                                          & (nb4 = ~(b4 = otherInits.extra[4 + 1][i]))
                                    | ((na4 = ~a4) | (na3 = ~a3)) 
                                          & ((na2 = ~(a2 = this.extra[2 + 1][i])) & nb2 
                                                | a2 & (nb3 = ~(b3 = otherInits.extra[3 + 1][i])) & nb4));
                  this.extra[2 + 1][i]  = b2 & (nb4 | nb3)
                                    | na3 & na4 & b2
                                    | a2 & (nb3 & nb4
                                                | (nb1 = ~b1) & (na3 | (na1 = ~a1))
                                                | a1 & b2);
                  this.extra[3 + 1][i] = b3 & (nb1 & (b2 | a2 | na1)
                                          | b1 & (b4 | nb2 | a1 & a3)
                                          | na1 & na2 & na4)
                                    | a3 & nb2 & nb4
                                    | nb1 & ((na2 & a4 | na1) & a3
                                                | a1 & na2 & na4 & b2);
                  this.extra[4 + 1][i] = nb1 & (a4 & (na3 & nb3   | (a3 | na2) & nb2)
                                    | a1 & (a3 & nb2 & b4
                                                | a2 & b2 & (b4   | a3 & na4 & nb3)))
                                    | b1 & (a3 & a4 & b4
                                          | na2 & na4 & nb3 & b4
                                          | a2 & ((b3 | a4) & b4
                                                      | na3 & a4 & b2 & b3)
                                          | na1 & (b4 | (a4 | a2) & b2 & b3))
                                    | (na1 & (na3 & nb3 | na2 & nb2)
                                          | a1 & (nb2 & nb3 | a2 & a3)) & b4; 
                  if (coverageTestFlag && coverageTestId == 7) {
                    this.extra[5][i] = ~0;
                  }
            }
            for (; i < copyLimit; i++) {
                  for (int j = 2; j < extraLength; j++) {
                        this.extra[j][i] = otherInits.extra[j][i];
                  }
                  if (coverageTestFlag && coverageTestId == 8) {
                    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);
      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
 */
00334 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 thisHadNulls = (this.tagBits & NULL_FLAG_MASK) != 0,
            thisHasNulls = false;
      long a1, a2, a3, a4,  
            na1, na2, na3, na4, 
            b1, b2, b3, b4,
            nb1, nb2, nb3, nb4;
      if (thisHadNulls) {
            this.nullBit1  = (a1 = this.nullBit1) 
                                                & ((a3 = this.nullBit3) & (a4 = this.nullBit4) 
                                                      & ((nb2 = ~(b2 = otherInits.nullBit2)) 
                                                            & (nb4 = ~(b4 = otherInits.nullBit4)) 
                                                                  | (b1 = otherInits.nullBit1) & (b3 = otherInits.nullBit3))
                              | (na2 = ~(a2 = this.nullBit2)) 
                                    & (b1 & b3 | ((na4 = ~a4) | (na3 = ~a3)) & nb2)
                              | a2 & ((na4 | na3) & ((nb3 = ~b3) & nb4 | b1 & b2)));
            this.nullBit2 = b2 & (nb3 | (nb1 = ~b1))
                  | a2 & (nb3 & nb4 | b2 | na3 | (na1 = ~a1));
            this.nullBit3 = b3 & (nb1 & b2
                        | a2 & (nb2 | a3)
                        | na1 & nb2
                        | a1 & na2 & na4 & b1)
                  | a3 & (nb2 & nb4 | na2 & a4 | na1)
                  | a1 & na2 & na4 & b2;
            this.nullBit4 = na3 & (nb1 & nb3 & b4
                        | a4 & (nb3 | b1 & b2))
                  | nb2 & (na3 & b1 & nb3 | na2 & (nb1 & b4 | b1 & nb3 | a4))
                  | a3 & (a4 & (nb2 | b1 & b3)
                              | a1 & a2 & (nb1 & b4 | na4 & (b2 | b1) & nb3));
            if (coverageTestFlag && coverageTestId == 9) {
              this.nullBit4 = ~0;
            }
            if ((this.nullBit2 | this.nullBit3 | this.nullBit4) != 0) { //  bit1 is redundant
                  thisHasNulls = true;
            }
      } else {
            this.nullBit1 = 0;
            this.nullBit2 = (b2 = otherInits.nullBit2) 
                                          & ((nb3 = ~(b3 = otherInits.nullBit3)) | 
                                                (nb1 = ~(b1 = otherInits.nullBit1)));
            this.nullBit3 = b3 & (nb1 | (nb2 = ~b2));
            this.nullBit4 = ~b1 & ~b3 & (b4 = otherInits.nullBit4) | ~b2 & (b1 & ~b3 | ~b1 & b4);
            if (coverageTestFlag && coverageTestId == 10) {
              this.nullBit4 = ~0;
            }
            if ((this.nullBit2 | this.nullBit3 | this.nullBit4) != 0) { //  bit1 is redundant
                  thisHasNulls = true;
            }
      }
      // extra storage management
      if (otherInits.extra != null) {
            int mergeLimit = 0, copyLimit = 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[copyLimit];
                  }
                  if (coverageTestFlag && coverageTestId == 11) {
                        throw new AssertionFailedException("COVERAGE 11"); //$NON-NLS-1$
                  }
            } else {
                  mergeLimit = copyLimit;
                  if (mergeLimit > this.extra[0].length) {
                        mergeLimit = this.extra[0].length;
                        for (int j = 0; j < extraLength; j++) {
                              System.arraycopy(this.extra[j], 0,
                                          this.extra[j] = new long[copyLimit], 0,
                                          mergeLimit);
                        }
                        if (! thisHadNulls) {
                        mergeLimit = 0; 
                        // will do with a copy -- caveat: only valid because definite assignment bits copied above
                        if (coverageTestFlag && coverageTestId == 12) {
                                    throw new AssertionFailedException("COVERAGE 12"); //$NON-NLS-1$
                        }
                        }
                  }
            }
            // PREMATURE skip operations for fields
            int i;
            for (i = 0 ; i < mergeLimit ; i++) {
            this.extra[1 + 1][i]  = (a1 = this.extra[1 + 1][i]) 
                                                & ((a3 = this.extra[3 + 1][i]) & (a4 = this.extra[4 + 1][i]) 
                                                      & ((nb2 = ~(b2 = otherInits.extra[2 + 1][i])) 
                                                            & (nb4 = ~(b4 = otherInits.extra[4 + 1][i])) 
                                                                  | (b1 = otherInits.extra[1 + 1][i]) & (b3 = otherInits.extra[3 + 1][i]))
                                    | (na2 = ~(a2 = this.extra[2 + 1][i])) 
                                          & (b1 & b3 | ((na4 = ~a4) | (na3 = ~a3)) & nb2)
                                    | a2 & ((na4 | na3) & ((nb3 = ~b3) & nb4 | b1 & b2)));
            this.extra[2 + 1][i] = b2 & (nb3 | (nb1 = ~b1))
                        | a2 & (nb3 & nb4 | b2 | na3 | (na1 = ~a1));
            this.extra[3 + 1][i] = b3 & (nb1 & b2
                        | a2 & (nb2 | a3)
                        | na1 & nb2
                        | a1 & na2 & na4 & b1)
                        | a3 & (nb2 & nb4 | na2 & a4 | na1)
                        | a1 & na2 & na4 & b2;
            this.extra[4 + 1][i] = na3 & (nb1 & nb3 & b4
                              | a4 & (nb3 | b1 & b2))
                        | nb2 & (na3 & b1 & nb3 | na2 & (nb1 & b4 | b1 & nb3 | a4))
                        | a3 & (a4 & (nb2 | b1 & b3)
                              | a1 & a2 & (nb1 & b4 | na4 & (b2 | b1) & nb3));
            if ((this.extra[2 + 1][i] | this.extra[3 + 1][i] | this.extra[4 + 1][i]) != 0) { //  bit1 is redundant
                  thisHasNulls = true;
            }
                  if (coverageTestFlag && coverageTestId == 13) {
                    this.nullBit4 = ~0;
                  }
            }
            for (; i < copyLimit; i++) {
            this.extra[1 + 1][i] = 0;
            this.extra[2 + 1][i] = (b2 = otherInits.extra[2 + 1][i]) 
                                          & ((nb3 = ~(b3 = otherInits.extra[3 + 1][i])) | 
                                                (nb1 = ~(b1 = otherInits.extra[1 + 1][i])));
            this.extra[3 + 1][i] = b3 & (nb1 | (nb2 = ~b2));
            this.extra[4 + 1][i] = ~b1 & ~b3 & (b4 = otherInits.extra[4 + 1][i]) | ~b2 & (b1 & ~b3 | ~b1 & b4);
            if ((this.extra[2 + 1][i] | this.extra[3 + 1][i] | this.extra[4 + 1][i]) != 0) { //  bit1 is redundant
                  thisHasNulls = true;
            }
                  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.nullBit1 = this.nullBit1;
            copy.nullBit2 = this.nullBit2;
            copy.nullBit3 = this.nullBit3;
            copy.nullBit4 = this.nullBit4;
      }
      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;
}

/**
 * Discard definite inits and potential inits from this, then return this.
 * The returned flow info only holds null related information. 
 * @return this flow info, minus definite inits and potential inits
 */
00520 public UnconditionalFlowInfo discardInitializationInfo() {
      if (this == DEAD_END) {
            return this;
      }
      this.definiteInits =
            this.potentialInits = 0;
      if (this.extra != null) {
            for (int i = 0, length = this.extra[0].length; i < length; i++) {
                  this.extra[0][i] = this.extra[1][i] = 0;
            }
      }
      return this;
}

/**
 * Remove local variables information from this flow info and return this.
 * @return this, deprived from any local variable information
 */
00538 public UnconditionalFlowInfo discardNonFieldInitializations() {
      int limit = this.maxFieldCount;
      if (limit < BitCacheSize) {
            long mask = (1L << limit)-1;
            this.definiteInits &= mask;
            this.potentialInits &= mask;
            this.nullBit1 &= mask;
            this.nullBit2 &= mask;
            this.nullBit3 &= mask;
            this.nullBit4 &= 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.
 */
00585 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 local declared in reachable code
      if ((this.tagBits & UNREACHABLE) != 0 && (local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 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) { // String instances
            return true;
      }
      int position = local.id + this.maxFieldCount;
      if (position < BitCacheSize) { // use bits
            return ((this.nullBit1 & this.nullBit3 & (~this.nullBit2 | this.nullBit4))
                      & (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[2][vectorIndex] & this.extra[4][vectorIndex]
                    & (~this.extra[3][vectorIndex] | this.extra[5][vectorIndex]))
                & (1L << (position % BitCacheSize))) != 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;
      if (position < BitCacheSize) { // use bits
            return ((this.nullBit1 & this.nullBit2
                          & (~this.nullBit3 | ~this.nullBit4))
                      & (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[2][vectorIndex] & this.extra[3][vectorIndex]
                    & (~this.extra[4][vectorIndex] | ~this.extra[5][vectorIndex]))
                & (1L << (position % BitCacheSize))) != 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;
      if (position < BitCacheSize) { // use bits
            return ((this.nullBit1 & this.nullBit4
                        & ~this.nullBit2 & ~this.nullBit3) & (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[2][vectorIndex] & this.extra[5][vectorIndex]
          & ~this.extra[3][vectorIndex] & ~this.extra[4][vectorIndex])
                & (1L << (position % BitCacheSize))) != 0;
}

/**
 * Check status of potential assignment at a given position.
 */
00707 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;
}

final public boolean isPotentiallyAssigned(FieldBinding field) {
      return isPotentiallyAssigned(field.id); 
}

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);
}

final public boolean isPotentiallyNonNull(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.nullBit3 & (~this.nullBit1 | ~this.nullBit2))
                      & (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[2][vectorIndex] | ~this.extra[3][vectorIndex]))
                & (1L << (position % BitCacheSize))) != 0;
}

final public boolean isPotentiallyNull(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.nullBit2 & (~this.nullBit1 | ~this.nullBit3))
                      & (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[3][vectorIndex]
                    & (~this.extra[2][vectorIndex] | ~this.extra[4][vectorIndex]))
                & (1L << (position % BitCacheSize))) != 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;
      if (position < BitCacheSize) { // use bits
            return (this.nullBit4 
                  & (~this.nullBit1 | ~this.nullBit2 & ~this.nullBit3)
                  & (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[5][vectorIndex]
              & (~this.extra[2][vectorIndex] 
                  | ~this.extra[3][vectorIndex] & ~this.extra[4][vectorIndex]) 
                & (1L << (position % BitCacheSize))) != 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.nullBit1 & this.nullBit3 & this.nullBit4 & (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[2][vectorIndex] 
                  & 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.nullBit1 & this.nullBit2
                  & (this.nullBit3 ^ this.nullBit4)
                  & (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[2][vectorIndex] & this.extra[3][vectorIndex]
              & (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;
            long a1, a2, a3, a4, na2;
            // position is zero-based
            if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
                  // use bits
                  if (((mask = 1L << position) 
                        & (a1 = this.nullBit1)
                        & (na2 = ~(a2 = this.nullBit2))
                        & ~(a3 = this.nullBit3)
                        & (a4 = this.nullBit4))
                              != 0) {
                        this.nullBit4 &= ~mask;
                  } else if ((mask & a1 & na2 & a3) == 0) {
                        this.nullBit4 |= mask;
                        if ((mask & a1) == 0) {
                              if ((mask & a2 & (a3 ^ a4)) != 0) {
                                    this.nullBit2 &= ~mask;
                              }
                              else if ((mask & (a2 | a3 | a4)) == 0) {
                                    this.nullBit2 |= mask;
                              }
                        }
                  }
                  this.nullBit1 |= mask;
                  this.nullBit3 |= mask; 
                  if (coverageTestFlag && coverageTestId == 15) {
                        this.nullBit4 = ~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 == 16) {
                              throw new AssertionFailedException("COVERAGE 16"); //$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 == 17) {
                                    throw new AssertionFailedException("COVERAGE 17"); //$NON-NLS-1$
                              }
                        }
                  }
                  // MACRO :'b,'es/nullBit\(.\)/extra[\1 + 1][vectorIndex]/gc
                  if (((mask = 1L << (position % BitCacheSize)) 
                        & (a1 = this.extra[1 + 1][vectorIndex])
                        & (na2 = ~(a2 = this.extra[2 + 1][vectorIndex]))
                        & ~(a3 = this.extra[3 + 1][vectorIndex])
                        & (a4 = this.extra[4 + 1][vectorIndex]))
                              != 0) {
                        this.extra[4 + 1][vectorIndex] &= ~mask;
                  } else if ((mask & a1 & na2 & a3) == 0) {
                        this.extra[4 + 1][vectorIndex] |= mask;
                        if ((mask & a1) == 0) {
                              if ((mask & a2 & (a3 ^ a4)) != 0) {
                                    this.extra[2 + 1][vectorIndex] &= ~mask;
                              }
                              else if ((mask & (a2 | a3 | a4)) == 0) {
                                    this.extra[2 + 1][vectorIndex] |= mask;
                              }
                        }
                  }
                  this.extra[1 + 1][vectorIndex] |= mask;
                  this.extra[3 + 1][vectorIndex] |= mask; 
                  if (coverageTestFlag && coverageTestId == 18) {
                        this.extra[5][vectorIndex] = ~0;
                  }
            }
      }
}

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;
            // position is zero-based
            if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
                  // use bits
                  if (((mask = 1L << position) & this.nullBit1) != 0) {
                        if ((mask  
                              & (~this.nullBit2 | this.nullBit3
                                    | ~this.nullBit4)) != 0) {
                              this.nullBit4 &= ~mask;
                        }
                  } else if ((mask & this.nullBit4) != 0) {
                          this.nullBit3 &= ~mask;
                  } else {
                  if ((mask & this.nullBit2) != 0) {
                        this.nullBit3 &= ~mask;
                              this.nullBit4 |= mask;
                  } else {
                        this.nullBit3 |= mask;
                  }
                  }
                  this.nullBit1 |= mask;
                  this.nullBit2 |= mask; 
                  if (coverageTestFlag && coverageTestId == 19) {
                        this.nullBit4 = ~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 == 20) {
                              throw new AssertionFailedException("COVERAGE 20"); //$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 == 21) {
                                    throw new AssertionFailedException("COVERAGE 21"); //$NON-NLS-1$
                              }
                        }
                  }
                  if ((mask & this.extra[1 + 1][vectorIndex]) != 0) {
                        if ((mask  
                              & (~this.extra[2 + 1][vectorIndex] | this.extra[3 + 1][vectorIndex]
                                    | ~this.extra[4 + 1][vectorIndex])) != 0) {
                              this.extra[4 + 1][vectorIndex] &= ~mask;
                        }
                  } else if ((mask & this.extra[4 + 1][vectorIndex]) != 0) {
                          this.extra[3 + 1][vectorIndex] &= ~mask;
                  } else {
                  if ((mask & this.extra[2 + 1][vectorIndex]) != 0) {
                        this.extra[3 + 1][vectorIndex] &= ~mask;
                              this.extra[4 + 1][vectorIndex] |= mask;
                  } else {
                        this.extra[3 + 1][vectorIndex] |= mask;
                  }
                  }
                  this.extra[1 + 1][vectorIndex] |= mask;
                  this.extra[2 + 1][vectorIndex] |= mask; 
            }
      }
}

/**
 * Record a definite assignment at a given position.
 */
01039 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);
}

public void markAsDefinitelyNonNull(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
            // set assigned non null
            this.nullBit1 |= (mask = 1L << position);
            this.nullBit3 |= mask;
            // clear others
            this.nullBit2 &= (mask = ~mask);
            this.nullBit4 &= mask;
            if (coverageTestFlag && coverageTestId == 22) {
                  this.nullBit1 = 0;
            }
      } 
      else {
            // use extra vector
            int vectorIndex ;
            this.extra[2][vectorIndex = (position / BitCacheSize) - 1] 
                |= (mask = 1L << (position % BitCacheSize));
            this.extra[4][vectorIndex] |= mask;
            this.extra[3][vectorIndex] &= (mask = ~mask);
            this.extra[5][vectorIndex] &= mask;
            if (coverageTestFlag && coverageTestId == 23) {
                  this.extra[2][vectorIndex] = 0;
            }
      }
      }
}

public void markAsDefinitelyNull(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
            // mark assigned null
            this.nullBit1 |= (mask = 1L << position);
            this.nullBit2 |= mask;
            // clear others
            this.nullBit3 &= (mask = ~mask);
            this.nullBit4 &= mask;
            if (coverageTestFlag && coverageTestId == 24) {
                  this.nullBit4 = ~0;
            }
      } 
      else {
            // use extra vector
            int vectorIndex ;
            this.extra[2][vectorIndex = (position / BitCacheSize) - 1] 
                |= (mask = 1L << (position % BitCacheSize));
            this.extra[3][vectorIndex] |= mask;
            this.extra[4][vectorIndex] &= (mask = ~mask);
            this.extra[5][vectorIndex] &= mask;
            if (coverageTestFlag && coverageTestId == 25) {
                  this.extra[5][vectorIndex] = ~0;
            }
      }
      }
}

/**
 * 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
01159 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
                  // mark assigned null
                  this.nullBit1 |= (mask = 1L << position);
                  this.nullBit4 |= mask;
                  // clear others
                  this.nullBit2 &= (mask = ~mask);
                  this.nullBit3 &= mask;
                  if (coverageTestFlag && coverageTestId == 26) {
                        this.nullBit4 = 0;
                  }
            } 
            else {
                  // use extra vector
                  int vectorIndex ;
                  this.extra[2][vectorIndex = (position / BitCacheSize) - 1] 
                      |= (mask = 1L << (position % BitCacheSize));
                  this.extra[5][vectorIndex] |= mask;
                  this.extra[3][vectorIndex] &= (mask = ~mask);
                  this.extra[4][vectorIndex] &= mask;
                  if (coverageTestFlag && coverageTestId == 27) {
                        this.extra[5][vectorIndex] = 0;
                  }
            }
      }
}

public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
      if ((otherInits.tagBits & UNREACHABLE) != 0 && this != DEAD_END) {
            if (coverageTestFlag && coverageTestId == 28) {
                  throw new AssertionFailedException("COVERAGE 28"); //$NON-NLS-1$
            }
            return this;
      }
      if ((this.tagBits & UNREACHABLE) != 0) {
            if (coverageTestFlag && coverageTestId == 29) {
                  throw new AssertionFailedException("COVERAGE 29"); //$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
            thisHasNulls = (this.tagBits & NULL_FLAG_MASK) != 0,
            otherHasNulls = (otherInits.tagBits & NULL_FLAG_MASK) != 0,
            thisHadNulls = thisHasNulls;
      long 
            a1, a2, a3, a4,  
            na1, na2, na3, na4, 
            nb1, nb2, nb3, nb4,
            b1, b2, b3, b4;
      if (thisHadNulls) {
      if (otherHasNulls) {
            this.nullBit1 = (a2 = this.nullBit2) & (a3 = this.nullBit3) 
                                          & (a4 = this.nullBit4) & (b1 = otherInits.nullBit1) 
                                          & (nb2 = ~(b2 = otherInits.nullBit2))
                                    | (a1 = this.nullBit1) & (b1 & (a3 & a4 & (b3 = otherInits.nullBit3) 
                                                                                                & (b4 = otherInits.nullBit4)
                                                                                          | (na2 = ~a2) & nb2 
                                                                                                & ((nb4 = ~b4) | (na4 = ~a4) 
                                                                                                            | (na3 = ~a3) & (nb3 = ~b3))
                                                                                          | a2 & b2 & ((na4 | na3) & (nb4     | nb3)))
                                                                                    | na2 & b2 & b3 & b4);
            this.nullBit2 = b2 & (nb3 | (nb1 = ~b1) | a3 & (a4 | (na1 = ~a1)) & nb4)
                        | a2 & (b2 | na4 & b3 & (b4 | nb1) | na3 | na1);
            this.nullBit3 = b3 & (nb2 & b4 | nb1 | a3 & (na4 & nb4 | a4 & b4))
                        | a3 & (na2 & a4 | na1)
                        | (a2 | na1) & b1 & nb2 & nb4
                        | a1 & na2 & na4 & (b2 | nb1);
            this.nullBit4 = na3 & (nb1 & nb3 & b4
                              | b1 & (nb2 & nb3 | a4 & b2 & nb4)
                              | na1 & a4 & (nb3 | b1 & b2))
                        | a3 & a4 & (b3 & b4 | b1 & nb2)
                        | na2 & (nb1 & b4 | b1 & nb3 | na1 & a4) & nb2
                        | a1 & (na3 & (nb3 & b4
                                          | b1 & b2 & b3 & nb4
                                          | na2 & (nb3 | nb2))
                              | na2 & b3 & b4
                              | a2 & (nb1 & b4 | a3 & na4 & b1) & nb3);
            if (coverageTestFlag && coverageTestId == 30) {
                  this.nullBit4 = ~0;
            }
      } else { // other has no null info 
            a1 = this.nullBit1;
                  this.nullBit1 = 0;
                  this.nullBit2 = (a2 = this.nullBit2) & (na3 = ~(a3 = this.nullBit3) | (na1 = ~a1));
                  this.nullBit3 = a3 & ((na2 = ~a2) & (a4 = this.nullBit4) | na1) | a1 & na2 & ~a4;
                  this.nullBit4 = (na3 | na2) & na1 & a4    | a1 & na3 & na2;
            if (coverageTestFlag && coverageTestId == 31) {
                  this.nullBit4 = ~0;
            }
      }
      } else if (otherHasNulls) { // only other had nulls
            this.nullBit1 = 0;
            this.nullBit2 = (b2 = otherInits.nullBit2) & (nb3 = ~(b3 = otherInits.nullBit3) | (nb1 = ~(b1 = otherInits.nullBit1)));
            this.nullBit3 = b3 & ((nb2 = ~b2) & (b4 = otherInits.nullBit4) | nb1) | b1 & nb2 & ~b4;
            this.nullBit4 = (nb3 | nb2) & nb1 & b4    | b1 & nb3 & nb2;
            if (coverageTestFlag && coverageTestId == 32) {
                  this.nullBit4 = ~0;
            }
      thisHasNulls =
            // redundant with the three following ones
            this.nullBit2 != 0 ||
            this.nullBit3 != 0 ||
            this.nullBit4 != 0;
      }

      // treating extra storage
      if (this.extra != null || otherInits.extra != null) {
            int mergeLimit = 0, copyLimit = 0, resetLimit = 0;
            int i;
            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 == 33) {
                                    throw new AssertionFailedException("COVERAGE 33"); //$NON-NLS-1$
                              }
                        } 
                        else {
                              // current storage is longer
                              mergeLimit = otherLength;
                              resetLimit = length;
                              if (coverageTestFlag && coverageTestId == 34) {
                                    throw new AssertionFailedException("COVERAGE 34"); //$NON-NLS-1$
                              }
                        }
                  } 
                  else {
                        resetLimit = this.extra[0].length;
                        if (coverageTestFlag && coverageTestId == 35) {
                              throw new AssertionFailedException("COVERAGE 35"); //$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 == 36) {
                        throw new AssertionFailedException("COVERAGE 36"); //$NON-NLS-1$
                  }
            }
        // MACRO :'b,'es/nullBit\(.\)/extra[\1 + 1][i]/g
            // manage definite assignment
            for (i = 0; i < mergeLimit; i++) {
                  this.extra[0][i] &= otherInits.extra[0][i];
                  this.extra[1][i] |= otherInits.extra[1][i];
            }
            for (; i < copyLimit; i++) {
                  this.extra[1][i] = otherInits.extra[1][i];
            }
            for (; i < resetLimit; i++) {
                  this.extra[0][i] = 0;
            }
            // refine null bits requirements
            if (!otherHasNulls) {
              if (resetLimit < mergeLimit) {
                  resetLimit = mergeLimit;
              }
              copyLimit = 0; // no need to carry inexisting nulls
              mergeLimit = 0;
            }
            if (!thisHadNulls) {
              resetLimit = 0; // no need to reset anything
            }
            // compose nulls
            for (i = 0; i < mergeLimit; i++) {
            this.extra[1 + 1][i] = (a2 = this.extra[2 + 1][i]) & (a3 = this.extra[3 + 1][i]) 
                                          & (a4 = this.extra[4 + 1][i]) & (b1 = otherInits.extra[1 + 1][i]) 
                                          & (nb2 = ~(b2 = otherInits.extra[2 + 1][i]))
                                    | (a1 = this.extra[1 + 1][i]) & (b1 & (a3 & a4 & (b3 = otherInits.extra[3 + 1][i]) 
                                                                                                & (b4 = otherInits.extra[4 + 1][i])
                                                                                          | (na2 = ~a2) & nb2 
                                                                                                & ((nb4 = ~b4) | (na4 = ~a4) 
                                                                                                            | (na3 = ~a3) & (nb3 = ~b3))
                                                                                          | a2 & b2 & ((na4 | na3) & (nb4     | nb3)))
                                                                                    | na2 & b2 & b3 & b4);
            this.extra[2 + 1][i] = b2 & (nb3 | (nb1 = ~b1) | a3 & (a4 | (na1 = ~a1)) & nb4)
                        | a2 & (b2 | na4 & b3 & (b4 | nb1) | na3 | na1);
            this.extra[3 + 1][i] = b3 & (nb2 & b4 | nb1 | a3 & (na4 & nb4 | a4 & b4))
                        | a3 & (na2 & a4 | na1)
                        | (a2 | na1) & b1 & nb2 & nb4
                        | a1 & na2 & na4 & (b2 | nb1);
            this.extra[4 + 1][i] = na3 & (nb1 & nb3 & b4
                              | b1 & (nb2 & nb3 | a4 & b2 & nb4)
                              | na1 & a4 & (nb3 | b1 & b2))
                        | a3 & a4 & (b3 & b4 | b1 & nb2)
                        | na2 & (nb1 & b4 | b1 & nb3 | na1 & a4) & nb2
                        | a1 & (na3 & (nb3 & b4
                                          | b1 & b2 & b3 & nb4
                                          | na2 & (nb3 | nb2))
                              | na2 & b3 & b4
                              | a2 & (nb1 & b4 | a3 & na4 & b1) & nb3);
                  thisHasNulls = thisHasNulls ||
                        this.extra[3][i] != 0 ||
                        this.extra[4][i] != 0 ||
                        this.extra[5][i] != 0 ;
                  if (coverageTestFlag && coverageTestId == 37) {
                        this.extra[5][i] = ~0;
                  }
            }
            for (; i < copyLimit; i++) {
            this.extra[1 + 1][i] = 0;
            this.extra[2 + 1][i] = (b2 = otherInits.extra[2 + 1][i]) & (nb3 = ~(b3 = otherInits.extra[3 + 1][i]) | (nb1 = ~(b1 = otherInits.extra[1 + 1][i])));
            this.extra[3 + 1][i] = b3 & ((nb2 = ~b2) & (b4 = otherInits.extra[4 + 1][i]) | nb1) | b1 & nb2 & ~b4;
            this.extra[4 + 1][i] = (nb3 | nb2) & nb1 & b4   | b1 & nb3 & nb2;
                  thisHasNulls = thisHasNulls ||
                        this.extra[3][i] != 0 ||
                        this.extra[4][i] != 0 ||
                        this.extra[5][i] != 0;
                  if (coverageTestFlag && coverageTestId == 38) {
                        this.extra[5][i] = ~0;
                  }
            }
            for (; i < resetLimit; i++) {
            a1 = this.extra[1 + 1][i];
                  this.extra[1 + 1][i] = 0;
                  this.extra[2 + 1][i] = (a2 = this.extra[2 + 1][i]) & (na3 = ~(a3 = this.extra[3 + 1][i]) | (na1 = ~a1));
                  this.extra[3 + 1][i] = a3 & ((na2 = ~a2) & (a4 = this.extra[4 + 1][i]) | na1) | a1 & na2 & ~a4;
                  this.extra[4 + 1][i] = (na3 | na2) & na1 & a4   | a1 & na3 & na2;
                  thisHasNulls = thisHasNulls ||
                        this.extra[3][i] != 0 ||
                        this.extra[4][i] != 0 ||
                        this.extra[5][i] != 0;
                  if (coverageTestFlag && coverageTestId == 39) {
                        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$
                        +", null: " + this.nullBit1 //$NON-NLS-1$
                              + this.nullBit2 + this.nullBit3 + this.nullBit4
                        +">"; //$NON-NLS-1$
            }
            else {
                  String def = "FlowInfo<def:[" + this.definiteInits, //$NON-NLS-1$
                        pot = "], pot:[" + this.potentialInits, //$NON-NLS-1$
                        nullS = ", null:[" + this.nullBit1 //$NON-NLS-1$
                              + this.nullBit2 + this.nullBit3 + this.nullBit4;
                  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$
                        nullS += "," + this.extra[2][i] //$NON-NLS-1$
                            + this.extra[3][i] + this.extra[4][i] + this.extra[5][i];
                  }
                  if (ceil < this.extra[0].length) {
                        def += ",..."; //$NON-NLS-1$
                        pot += ",..."; //$NON-NLS-1$
                        nullS += ",..."; //$NON-NLS-1$
                  }
                  return def + pot 
                        + "], reachable:" + ((this.tagBits & UNREACHABLE) == 0) //$NON-NLS-1$
                        + nullS
                        + "]>"; //$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.nullBit1 = this.nullBit1 & mask;
            copy.nullBit2 = this.nullBit2 & mask;
            copy.nullBit3 = this.nullBit3 & mask;
            copy.nullBit4 = this.nullBit4 & 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;
}
}


Generated by  Doxygen 1.6.0   Back to index