1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.compiler.flow;
 
  13 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
 
  14 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
 
  15 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
 
  16 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
 
  19  * Record initialization status during definite assignment analysis
 
  21  * No caching of pre-allocated instances.
 
  23 public class UnconditionalFlowInfo extends FlowInfo {
 
  25         public long definiteInits;
 
  27         public long potentialInits;
 
  29         public long extraDefiniteInits[];
 
  31         public long extraPotentialInits[];
 
  33         public int reachMode = REACHABLE; // by default
 
  35         public int maxFieldCount;
 
  38         public static final int BitCacheSize = 64; // 64 bits in a long.
 
  40         UnconditionalFlowInfo() {
 
  43         // unions of both sets of initialization - used for try/finally
 
  44         public FlowInfo addInitializationsFrom(FlowInfo inits) {
 
  49                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
 
  50                 if (otherInits == DEAD_END)
 
  53                 // union of definitely assigned variables,
 
  54                 definiteInits |= otherInits.definiteInits;
 
  55                 // union of potentially set ones
 
  56                 potentialInits |= otherInits.potentialInits;
 
  58                 // treating extra storage
 
  59                 if (extraDefiniteInits != null) {
 
  60                         if (otherInits.extraDefiniteInits != null) {
 
  61                                 // both sides have extra storage
 
  62                                 int i = 0, length, otherLength;
 
  63                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
  64                                         // current storage is shorter -> grow current (could maybe
 
  65                                         // reuse otherInits extra storage?)
 
  66                                         System.arraycopy(extraDefiniteInits, 0,
 
  67                                                         (extraDefiniteInits = new long[otherLength]), 0,
 
  69                                         System.arraycopy(extraPotentialInits, 0,
 
  70                                                         (extraPotentialInits = new long[otherLength]), 0,
 
  73                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
 
  74                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
  76                                         while (i < otherLength) {
 
  77                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
  80                                         // current storage is longer
 
  81                                         while (i < otherLength) {
 
  82                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
 
  83                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
  86                                                 extraDefiniteInits[i++] = 0;
 
  89                                 // no extra storage on otherInits
 
  91                 } else if (otherInits.extraDefiniteInits != null) {
 
  92                         // no storage here, but other has extra storage.
 
  96                                                         otherInits.extraDefiniteInits,
 
  98                                                         (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]),
 
 100                         System.arraycopy(otherInits.extraPotentialInits, 0,
 
 101                                         (extraPotentialInits = new long[otherLength]), 0,
 
 107         // unions of both sets of initialization - used for try/finally
 
 108         public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
 
 110                 if (this == DEAD_END) {
 
 114                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
 
 115                 if (otherInits == DEAD_END) {
 
 118                 // union of potentially set ones
 
 119                 potentialInits |= otherInits.potentialInits;
 
 121                 // treating extra storage
 
 122                 if (extraDefiniteInits != null) {
 
 123                         if (otherInits.extraDefiniteInits != null) {
 
 124                                 // both sides have extra storage
 
 125                                 int i = 0, length, otherLength;
 
 126                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
 127                                         // current storage is shorter -> grow current (could maybe
 
 128                                         // reuse otherInits extra storage?)
 
 129                                         System.arraycopy(extraDefiniteInits, 0,
 
 130                                                         (extraDefiniteInits = new long[otherLength]), 0,
 
 132                                         System.arraycopy(extraPotentialInits, 0,
 
 133                                                         (extraPotentialInits = new long[otherLength]), 0,
 
 136                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 138                                         while (i < otherLength) {
 
 139                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
 142                                         // current storage is longer
 
 143                                         while (i < otherLength) {
 
 144                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 148                 } else if (otherInits.extraDefiniteInits != null) {
 
 149                         // no storage here, but other has extra storage.
 
 151                         extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
 
 152                         System.arraycopy(otherInits.extraPotentialInits, 0,
 
 153                                         (extraPotentialInits = new long[otherLength]), 0,
 
 159         // Report an error if necessary
 
 160         // public boolean complainIfUnreachable(Statement statement, BlockScope
 
 161         // scope, boolean didAlreadyComplain) {
 
 163         // if ((this.reachMode & UNREACHABLE) != 0) {
 
 164         // statement.bits &= ~ASTNode.IsReachableMASK;
 
 165         // boolean reported = this == DEAD_END;
 
 166         // if (!didAlreadyComplain && reported) {
 
 167         // scope.problemReporter().unreachableCode(statement);
 
 169         // return reported; // keep going for fake reachable
 
 175          * Answers a copy of the current instance
 
 177         public FlowInfo copy() {
 
 179                 // do not clone the DeadEnd
 
 180                 if (this == DEAD_END)
 
 183                 // look for an unused preallocated object
 
 184                 UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
 
 187                 copy.definiteInits = this.definiteInits;
 
 188                 copy.potentialInits = this.potentialInits;
 
 189                 copy.reachMode = this.reachMode;
 
 190                 copy.maxFieldCount = this.maxFieldCount;
 
 192                 if (this.extraDefiniteInits != null) {
 
 196                                                         this.extraDefiniteInits,
 
 198                                                         (copy.extraDefiniteInits = new long[(length = extraDefiniteInits.length)]),
 
 200                         System.arraycopy(this.extraPotentialInits, 0,
 
 201                                         (copy.extraPotentialInits = new long[length]), 0, length);
 
 207         public UnconditionalFlowInfo discardFieldInitializations() {
 
 209                 int limit = this.maxFieldCount;
 
 211                 if (limit < BitCacheSize) {
 
 212                         long mask = (1L << limit) - 1;
 
 213                         this.definiteInits &= ~mask;
 
 214                         this.potentialInits &= ~mask;
 
 218                 this.definiteInits = 0;
 
 219                 this.potentialInits = 0;
 
 222                 if (extraDefiniteInits == null) {
 
 223                         return this; // if vector not yet allocated, then not initialized
 
 225                 int vectorIndex, length = this.extraDefiniteInits.length;
 
 226                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
 
 227                         return this; // not enough room yet
 
 229                 for (int i = 0; i < vectorIndex; i++) {
 
 230                         this.extraDefiniteInits[i] = 0L;
 
 231                         this.extraPotentialInits[i] = 0L;
 
 233                 long mask = (1L << (limit % BitCacheSize)) - 1;
 
 234                 this.extraDefiniteInits[vectorIndex] &= ~mask;
 
 235                 this.extraPotentialInits[vectorIndex] &= ~mask;
 
 239         public UnconditionalFlowInfo discardNonFieldInitializations() {
 
 241                 int limit = this.maxFieldCount;
 
 243                 if (limit < BitCacheSize) {
 
 244                         long mask = (1L << limit) - 1;
 
 245                         this.definiteInits &= mask;
 
 246                         this.potentialInits &= mask;
 
 250                 if (extraDefiniteInits == null) {
 
 251                         return this; // if vector not yet allocated, then not initialized
 
 253                 int vectorIndex, length = this.extraDefiniteInits.length;
 
 254                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
 
 255                         return this; // not enough room yet
 
 257                 long mask = (1L << (limit % BitCacheSize)) - 1;
 
 258                 this.extraDefiniteInits[vectorIndex] &= mask;
 
 259                 this.extraPotentialInits[vectorIndex] &= mask;
 
 260                 for (int i = vectorIndex + 1; i < length; i++) {
 
 261                         this.extraDefiniteInits[i] = 0L;
 
 262                         this.extraPotentialInits[i] = 0L;
 
 267         public FlowInfo initsWhenFalse() {
 
 272         public FlowInfo initsWhenTrue() {
 
 278          * Check status of definite assignment at a given position. It deals with
 
 279          * the dual representation of the InitializationInfo2: bits for the first 64
 
 280          * entries, then an array of booleans.
 
 282         final private boolean isDefinitelyAssigned(int position) {
 
 284                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 286                 if (position < BitCacheSize) {
 
 287                         return (definiteInits & (1L << position)) != 0; // use bits
 
 290                 if (extraDefiniteInits == null)
 
 291                         return false; // if vector not yet allocated, then not initialized
 
 293                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length)
 
 294                         return false; // if not enough room in vector, then not
 
 296                 return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
 
 300          * Check status of definite assignment for a field.
 
 302         final public boolean isDefinitelyAssigned(FieldBinding field) {
 
 304                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 305                 // We do not want to complain in unreachable code
 
 306                 if ((this.reachMode & UNREACHABLE) != 0)
 
 308                 return isDefinitelyAssigned(field.id);
 
 312          * Check status of definite assignment for a local.
 
 314         final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
 
 316                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 317                 // We do not want to complain in unreachable code
 
 318                 if ((this.reachMode & UNREACHABLE) != 0)
 
 320                 if (local.isArgument) {
 
 323                 // final constants are inlined, and thus considered as always
 
 325                 if (local.constant != Constant.NotAConstant) {
 
 328                 return isDefinitelyAssigned(local.id + maxFieldCount);
 
 331         public boolean isReachable() {
 
 333                 return this.reachMode == REACHABLE;
 
 337          * Check status of potential assignment at a given position. It deals with
 
 338          * the dual representation of the InitializationInfo3: bits for the first 64
 
 339          * entries, then an array of booleans.
 
 341         final private boolean isPotentiallyAssigned(int position) {
 
 344                 if (position < BitCacheSize) {
 
 346                         return (potentialInits & (1L << position)) != 0;
 
 349                 if (extraPotentialInits == null)
 
 350                         return false; // if vector not yet allocated, then not initialized
 
 352                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length)
 
 353                         return false; // if not enough room in vector, then not
 
 355                 return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
 
 359          * Check status of definite assignment for a field.
 
 361         final public boolean isPotentiallyAssigned(FieldBinding field) {
 
 363                 return isPotentiallyAssigned(field.id);
 
 367          * Check status of potential assignment for a local.
 
 369         final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
 
 371                 if (local.isArgument) {
 
 374                 // final constants are inlined, and thus considered as always
 
 376                 if (local.constant != Constant.NotAConstant) {
 
 379                 return isPotentiallyAssigned(local.id + maxFieldCount);
 
 383          * Record a definite assignment at a given position. It deals with the dual
 
 384          * representation of the InitializationInfo2: bits for the first 64 entries,
 
 385          * then an array of booleans.
 
 387         final private void markAsDefinitelyAssigned(int position) {
 
 389                 if (this != DEAD_END) {
 
 391                         // position is zero-based
 
 392                         if (position < BitCacheSize) {
 
 395                                 definiteInits |= (mask = 1L << position);
 
 396                                 potentialInits |= mask;
 
 399                                 int vectorIndex = (position / BitCacheSize) - 1;
 
 400                                 if (extraDefiniteInits == null) {
 
 402                                         extraDefiniteInits = new long[length = vectorIndex + 1];
 
 403                                         extraPotentialInits = new long[length];
 
 405                                         int oldLength; // might need to grow the arrays
 
 406                                         if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
 
 411                                                                                 (extraDefiniteInits = new long[vectorIndex + 1]),
 
 417                                                                                 (extraPotentialInits = new long[vectorIndex + 1]),
 
 422                                 extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
 
 423                                 extraPotentialInits[vectorIndex] |= mask;
 
 429          * Record a field got definitely assigned.
 
 431         public void markAsDefinitelyAssigned(FieldBinding field) {
 
 432                 if (this != DEAD_END)
 
 433                         markAsDefinitelyAssigned(field.id);
 
 437          * Record a local got definitely assigned.
 
 439         public void markAsDefinitelyAssigned(LocalVariableBinding local) {
 
 440                 if (this != DEAD_END)
 
 441                         markAsDefinitelyAssigned(local.id + maxFieldCount);
 
 445          * Clear initialization information at a given position. It deals with the
 
 446          * dual representation of the InitializationInfo2: bits for the first 64
 
 447          * entries, then an array of booleans.
 
 449         final private void markAsDefinitelyNotAssigned(int position) {
 
 450                 if (this != DEAD_END) {
 
 452                         // position is zero-based
 
 453                         if (position < BitCacheSize) {
 
 456                                 definiteInits &= ~(mask = 1L << position);
 
 457                                 potentialInits &= ~mask;
 
 460                                 int vectorIndex = (position / BitCacheSize) - 1;
 
 461                                 if (extraDefiniteInits == null) {
 
 462                                         return; // nothing to do, it was not yet set
 
 464                                         // might need to grow the arrays
 
 465                                         if (vectorIndex >= extraDefiniteInits.length) {
 
 466                                                 return; // nothing to do, it was not yet set
 
 470                                 extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
 
 471                                 extraPotentialInits[vectorIndex] &= ~mask;
 
 477          * Clear the initialization info for a field
 
 479         public void markAsDefinitelyNotAssigned(FieldBinding field) {
 
 481                 if (this != DEAD_END)
 
 482                         markAsDefinitelyNotAssigned(field.id);
 
 486          * Clear the initialization info for a local variable
 
 489         public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
 
 491                 if (this != DEAD_END)
 
 492                         markAsDefinitelyNotAssigned(local.id + maxFieldCount);
 
 496          * Returns the receiver updated in the following way:
 
 498          * <li> intersection of definitely assigned variables,
 
 499          * <li> union of potentially assigned variables.
 
 502         public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
 
 504                 if (this == DEAD_END)
 
 506                 if (otherInits == DEAD_END)
 
 509                 if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)) {
 
 510                         if ((this.reachMode & UNREACHABLE) != 0) {
 
 517                 // if one branch is not fake reachable, then the merged one is reachable
 
 518                 this.reachMode &= otherInits.reachMode;
 
 520                 // intersection of definitely assigned variables,
 
 521                 this.definiteInits &= otherInits.definiteInits;
 
 522                 // union of potentially set ones
 
 523                 this.potentialInits |= otherInits.potentialInits;
 
 525                 // treating extra storage
 
 526                 if (this.extraDefiniteInits != null) {
 
 527                         if (otherInits.extraDefiniteInits != null) {
 
 528                                 // both sides have extra storage
 
 529                                 int i = 0, length, otherLength;
 
 530                                 if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
 531                                         // current storage is shorter -> grow current (could maybe
 
 532                                         // reuse otherInits extra storage?)
 
 533                                         System.arraycopy(this.extraDefiniteInits, 0,
 
 534                                                         (this.extraDefiniteInits = new long[otherLength]),
 
 536                                         System.arraycopy(this.extraPotentialInits, 0,
 
 537                                                         (this.extraPotentialInits = new long[otherLength]),
 
 540                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
 
 541                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 543                                         while (i < otherLength) {
 
 544                                                 this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
 547                                         // current storage is longer
 
 548                                         while (i < otherLength) {
 
 549                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
 
 550                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 553                                                 this.extraDefiniteInits[i++] = 0;
 
 556                                 // no extra storage on otherInits
 
 557                                 int i = 0, length = this.extraDefiniteInits.length;
 
 559                                         this.extraDefiniteInits[i++] = 0;
 
 561                 } else if (otherInits.extraDefiniteInits != null) {
 
 562                         // no storage here, but other has extra storage.
 
 564                         this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
 
 565                         System.arraycopy(otherInits.extraPotentialInits, 0,
 
 566                                         (this.extraPotentialInits = new long[otherLength]), 0,
 
 573          * Answer the total number of fields in enclosing types of a given type
 
 575         static int numberOfEnclosingFields(ReferenceBinding type) {
 
 578                 type = type.enclosingType();
 
 579                 while (type != null) {
 
 580                         count += type.fieldCount();
 
 581                         type = type.enclosingType();
 
 586         public int reachMode() {
 
 587                 return this.reachMode;
 
 590         public FlowInfo setReachMode(int reachMode) {
 
 592                 if (this == DEAD_END)
 
 593                         return this; // cannot modify DEAD_END
 
 595                 // reset optional inits when becoming unreachable
 
 596                 if ((this.reachMode & UNREACHABLE) == 0
 
 597                                 && (reachMode & UNREACHABLE) != 0) {
 
 598                         this.potentialInits = 0;
 
 599                         if (this.extraPotentialInits != null) {
 
 600                                 for (int i = 0, length = this.extraPotentialInits.length; i < length; i++) {
 
 601                                         this.extraPotentialInits[i] = 0;
 
 605                 this.reachMode = reachMode;
 
 610         public String toString() {
 
 612                 if (this == DEAD_END) {
 
 613                         return "FlowInfo.DEAD_END"; //$NON-NLS-1$
 
 615                 return "FlowInfo<def: " + this.definiteInits //$NON-NLS-1$
 
 616                                 + ", pot: " + this.potentialInits //$NON-NLS-1$
 
 617                                 + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
 
 621         public UnconditionalFlowInfo unconditionalInits() {
 
 623                 // also see conditional inits, where it requests them to merge