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.BlockScope;
 
  15 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
 
  16 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
 
  17 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
 
  18 import net.sourceforge.phpeclipse.internal.compiler.ast.AstNode;
 
  19 import net.sourceforge.phpeclipse.internal.compiler.ast.Statement;
 
  22  * Record initialization status during definite assignment analysis
 
  24  * No caching of pre-allocated instances.
 
  26 public class UnconditionalFlowInfo extends FlowInfo {
 
  29         public long definiteInits;
 
  30         public long potentialInits;
 
  31         public long extraDefiniteInits[];
 
  32         public long extraPotentialInits[];
 
  34         public int reachMode = REACHABLE; // by default
 
  36         public int maxFieldCount;
 
  39         public static final int BitCacheSize = 64; // 64 bits in a long.
 
  41         UnconditionalFlowInfo() {
 
  44         // unions of both sets of initialization - used for try/finally
 
  45         public FlowInfo addInitializationsFrom(FlowInfo inits) {
 
  50                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();  
 
  51                 if (otherInits == DEAD_END)
 
  54                 // union of definitely assigned variables, 
 
  55                 definiteInits |= otherInits.definiteInits;
 
  56                 // union of potentially set ones
 
  57                 potentialInits |= otherInits.potentialInits;
 
  59                 // treating extra storage
 
  60                 if (extraDefiniteInits != null) {
 
  61                         if (otherInits.extraDefiniteInits != null) {
 
  62                                 // both sides have extra storage
 
  63                                 int i = 0, length, otherLength;
 
  64                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
  65                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
 
  66                                         System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
 
  67                                         System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
 
  69                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
 
  70                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
  72                                         while (i < otherLength) {
 
  73                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
  76                                         // current storage is longer
 
  77                                         while (i < otherLength) {
 
  78                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
 
  79                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
  82                                                 extraDefiniteInits[i++] = 0;
 
  85                                 // no extra storage on otherInits
 
  88                         if (otherInits.extraDefiniteInits != null) {
 
  89                                 // no storage here, but other has extra storage.
 
  91                                 System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength);                        
 
  92                                 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
 
  97         // unions of both sets of initialization - used for try/finally
 
  98         public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
 
 100                 if (this == DEAD_END){
 
 104                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
 
 105                 if (otherInits == DEAD_END){
 
 108                 // union of potentially set ones
 
 109                 potentialInits |= otherInits.potentialInits;
 
 111                 // treating extra storage
 
 112                 if (extraDefiniteInits != null) {
 
 113                         if (otherInits.extraDefiniteInits != null) {
 
 114                                 // both sides have extra storage
 
 115                                 int i = 0, length, otherLength;
 
 116                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
 117                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
 
 118                                         System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
 
 119                                         System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
 
 121                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 123                                         while (i < otherLength) {
 
 124                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
 127                                         // current storage is longer
 
 128                                         while (i < otherLength) {
 
 129                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 134                         if (otherInits.extraDefiniteInits != null) {
 
 135                                 // no storage here, but other has extra storage.
 
 137                                 extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];                      
 
 138                                 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
 
 143         // Report an error if necessary
 
 144         public boolean complainIfUnreachable(Statement statement, BlockScope scope, boolean didAlreadyComplain) {
 
 146                 if ((this.reachMode & UNREACHABLE) != 0) {
 
 147                         statement.bits &= ~AstNode.IsReachableMASK;
 
 148                         boolean reported = this == DEAD_END;
 
 149                         if (!didAlreadyComplain && reported) {
 
 150                                 scope.problemReporter().unreachableCode(statement);
 
 152                         return reported; // keep going for fake reachable
 
 158          * Answers a copy of the current instance
 
 160         public FlowInfo copy() {
 
 162                 // do not clone the DeadEnd
 
 163                 if (this == DEAD_END)
 
 166                 // look for an unused preallocated object
 
 167                 UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
 
 170                 copy.definiteInits = this.definiteInits;
 
 171                 copy.potentialInits = this.potentialInits;
 
 172                 copy.reachMode = this.reachMode;
 
 173                 copy.maxFieldCount = this.maxFieldCount;
 
 175                 if (this.extraDefiniteInits != null) {
 
 177                         System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[ (length = extraDefiniteInits.length)]), 0, length);
 
 178                         System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length);
 
 183         public UnconditionalFlowInfo discardFieldInitializations(){
 
 185                 int limit = this.maxFieldCount;
 
 187                 if (limit < BitCacheSize) {
 
 188                         long mask = (1L << limit)-1;
 
 189                         this.definiteInits &= ~mask;
 
 190                         this.potentialInits &= ~mask;
 
 194                 this.definiteInits = 0;
 
 195                 this.potentialInits = 0;
 
 198                 if (extraDefiniteInits == null) {
 
 199                         return this; // if vector not yet allocated, then not initialized
 
 201                 int vectorIndex, length = this.extraDefiniteInits.length;
 
 202                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
 
 203                         return this; // not enough room yet
 
 205                 for (int i = 0; i < vectorIndex; i++) {
 
 206                         this.extraDefiniteInits[i] = 0L;
 
 207                         this.extraPotentialInits[i] = 0L;
 
 209                 long mask = (1L << (limit % BitCacheSize))-1;
 
 210                 this.extraDefiniteInits[vectorIndex] &= ~mask;
 
 211                 this.extraPotentialInits[vectorIndex] &= ~mask;
 
 215         public UnconditionalFlowInfo discardNonFieldInitializations(){
 
 217                 int limit = this.maxFieldCount;
 
 219                 if (limit < BitCacheSize) {
 
 220                         long mask = (1L << limit)-1;
 
 221                         this.definiteInits &= mask;
 
 222                         this.potentialInits &= mask;
 
 226                 if (extraDefiniteInits == null) {
 
 227                         return this; // if vector not yet allocated, then not initialized
 
 229                 int vectorIndex, length = this.extraDefiniteInits.length;
 
 230                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
 
 231                         return this; // not enough room yet
 
 233                 long mask = (1L << (limit % BitCacheSize))-1;
 
 234                 this.extraDefiniteInits[vectorIndex] &= mask;
 
 235                 this.extraPotentialInits[vectorIndex] &= mask;
 
 236                 for (int i = vectorIndex+1; i < length; i++) {
 
 237                         this.extraDefiniteInits[i] = 0L;
 
 238                         this.extraPotentialInits[i] = 0L;
 
 243         public FlowInfo initsWhenFalse() {
 
 248         public FlowInfo initsWhenTrue() {
 
 254          * Check status of definite assignment at a given position.
 
 255          * It deals with the dual representation of the InitializationInfo2:
 
 256          * bits for the first 64 entries, then an array of booleans.
 
 258         final private boolean isDefinitelyAssigned(int position) {
 
 260                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 262                 if (position < BitCacheSize) {
 
 263                         return (definiteInits & (1L << position)) != 0; // use bits
 
 266                 if (extraDefiniteInits == null)
 
 267                         return false; // if vector not yet allocated, then not initialized
 
 269                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length)
 
 270                         return false; // if not enough room in vector, then not initialized 
 
 271                 return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
 
 275          * Check status of definite assignment for a field.
 
 277         final public boolean isDefinitelyAssigned(FieldBinding field) {
 
 279                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 280                 // We do not want to complain in unreachable code
 
 281                 if ((this.reachMode & UNREACHABLE) != 0)  
 
 283                 return isDefinitelyAssigned(field.id); 
 
 287          * Check status of definite assignment for a local.
 
 289         final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
 
 291                 // Dependant of CodeStream.isDefinitelyAssigned(..)
 
 292                 // We do not want to complain in unreachable code
 
 293                 if ((this.reachMode & UNREACHABLE) != 0)
 
 295                 if (local.isArgument) {
 
 298                 // final constants are inlined, and thus considered as always initialized
 
 299                 if (local.constant != Constant.NotAConstant) {
 
 302                 return isDefinitelyAssigned(local.id + maxFieldCount);
 
 305         public boolean isReachable() {
 
 307                 return this.reachMode == REACHABLE;
 
 311          * Check status of potential assignment at a given position.
 
 312          * It deals with the dual representation of the InitializationInfo3:
 
 313          * bits for the first 64 entries, then an array of booleans.
 
 315         final private boolean isPotentiallyAssigned(int position) {
 
 318                 if (position < BitCacheSize) {
 
 320                         return (potentialInits & (1L << position)) != 0;
 
 323                 if (extraPotentialInits == null)
 
 324                         return false; // if vector not yet allocated, then not initialized
 
 326                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length)
 
 327                         return false; // if not enough room in vector, then not initialized 
 
 328                 return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
 
 332          * Check status of definite assignment for a field.
 
 334         final public boolean isPotentiallyAssigned(FieldBinding field) {
 
 336                 return isPotentiallyAssigned(field.id); 
 
 340          * Check status of potential assignment for a local.
 
 342         final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
 
 344                 if (local.isArgument) {
 
 347                 // final constants are inlined, and thus considered as always initialized
 
 348                 if (local.constant != Constant.NotAConstant) {
 
 351                 return isPotentiallyAssigned(local.id + maxFieldCount);
 
 355          * Record a definite assignment at a given position.
 
 356          * It deals with the dual representation of the InitializationInfo2:
 
 357          * bits for the first 64 entries, then an array of booleans.
 
 359         final private void markAsDefinitelyAssigned(int position) {
 
 361                 if (this != DEAD_END) {
 
 363                         // position is zero-based
 
 364                         if (position < BitCacheSize) {
 
 367                                 definiteInits |= (mask = 1L << position);
 
 368                                 potentialInits |= mask;
 
 371                                 int vectorIndex = (position / BitCacheSize) - 1;
 
 372                                 if (extraDefiniteInits == null) {
 
 374                                         extraDefiniteInits = new long[length = vectorIndex + 1];
 
 375                                         extraPotentialInits = new long[length];
 
 377                                         int oldLength; // might need to grow the arrays
 
 378                                         if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
 
 379                                                 System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength);
 
 380                                                 System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength);
 
 384                                 extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
 
 385                                 extraPotentialInits[vectorIndex] |= mask;
 
 391          * Record a field got definitely assigned.
 
 393         public void markAsDefinitelyAssigned(FieldBinding field) {
 
 394                 if (this != DEAD_END)
 
 395                         markAsDefinitelyAssigned(field.id);
 
 399          * Record a local got definitely assigned.
 
 401         public void markAsDefinitelyAssigned(LocalVariableBinding local) {
 
 402                 if (this != DEAD_END)
 
 403                         markAsDefinitelyAssigned(local.id + maxFieldCount);
 
 407          * Clear initialization information at a given position.
 
 408          * It deals with the dual representation of the InitializationInfo2:
 
 409          * bits for the first 64 entries, then an array of booleans.
 
 411         final private void markAsDefinitelyNotAssigned(int position) {
 
 412                 if (this != DEAD_END) {
 
 414                         // position is zero-based
 
 415                         if (position < BitCacheSize) {
 
 418                                 definiteInits &= ~(mask = 1L << position);
 
 419                                 potentialInits &= ~mask;
 
 422                                 int vectorIndex = (position / BitCacheSize) - 1;
 
 423                                 if (extraDefiniteInits == null) {
 
 424                                         return; // nothing to do, it was not yet set 
 
 426                                         // might need to grow the arrays
 
 427                                         if (vectorIndex >= extraDefiniteInits.length) {
 
 428                                                 return; // nothing to do, it was not yet set 
 
 432                                 extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
 
 433                                 extraPotentialInits[vectorIndex] &= ~mask;
 
 439          * Clear the initialization info for a field
 
 441         public void markAsDefinitelyNotAssigned(FieldBinding field) {
 
 443                 if (this != DEAD_END)
 
 444                         markAsDefinitelyNotAssigned(field.id);
 
 448          * Clear the initialization info for a local variable
 
 451         public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
 
 453                 if (this != DEAD_END)
 
 454                         markAsDefinitelyNotAssigned(local.id + maxFieldCount);
 
 458          * Returns the receiver updated in the following way: <ul>
 
 459          * <li> intersection of definitely assigned variables, 
 
 460          * <li> union of potentially assigned variables.
 
 463         public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
 
 465                 if (this == DEAD_END) return otherInits;
 
 466                 if (otherInits == DEAD_END) return this;
 
 468                 if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){
 
 469                         if ((this.reachMode & UNREACHABLE) != 0){
 
 476                 // if one branch is not fake reachable, then the merged one is reachable
 
 477                 this.reachMode &= otherInits.reachMode;
 
 479                 // intersection of definitely assigned variables, 
 
 480                 this.definiteInits &= otherInits.definiteInits;
 
 481                 // union of potentially set ones
 
 482                 this.potentialInits |= otherInits.potentialInits;
 
 484                 // treating extra storage
 
 485                 if (this.extraDefiniteInits != null) {
 
 486                         if (otherInits.extraDefiniteInits != null) {
 
 487                                 // both sides have extra storage
 
 488                                 int i = 0, length, otherLength;
 
 489                                 if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
 
 490                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
 
 491                                         System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
 
 492                                         System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
 
 494                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
 
 495                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 497                                         while (i < otherLength) {
 
 498                                                 this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
 
 501                                         // current storage is longer
 
 502                                         while (i < otherLength) {
 
 503                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
 
 504                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
 
 507                                                 this.extraDefiniteInits[i++] = 0;
 
 510                                 // no extra storage on otherInits
 
 511                                 int i = 0, length = this.extraDefiniteInits.length;
 
 513                                         this.extraDefiniteInits[i++] = 0;
 
 516                         if (otherInits.extraDefiniteInits != null) {
 
 517                                 // no storage here, but other has extra storage.
 
 519                                 this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
 
 520                                 System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
 
 526          * Answer the total number of fields in enclosing types of a given type
 
 528         static int numberOfEnclosingFields(ReferenceBinding type){
 
 531                 type = type.enclosingType();
 
 532                 while(type != null) {
 
 533                         count += type.fieldCount();
 
 534                         type = type.enclosingType();
 
 539         public int reachMode(){
 
 540                 return this.reachMode;
 
 543         public FlowInfo setReachMode(int reachMode) {
 
 545                 if (this == DEAD_END) return this; // cannot modify DEAD_END
 
 547                 // reset optional inits when becoming unreachable
 
 548                 if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) {
 
 549                         this.potentialInits = 0;
 
 550                         if (this.extraPotentialInits != null){
 
 551                                 for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){
 
 552                                         this.extraPotentialInits[i] = 0;
 
 556                 this.reachMode = reachMode;
 
 561         public String toString(){
 
 563                 if (this == DEAD_END){
 
 564                         return "FlowInfo.DEAD_END"; //$NON-NLS-1$
 
 566                 return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$
 
 567                         +", pot: " + this.potentialInits  //$NON-NLS-1$
 
 568                         + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
 
 572         public UnconditionalFlowInfo unconditionalInits() {
 
 574                 // also see conditional inits, where it requests them to merge