Added patch from #1437426: error in assign template
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / flow / UnconditionalFlowInfo.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.flow;
12
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;
17
18 /**
19  * Record initialization status during definite assignment analysis
20  *
21  * No caching of pre-allocated instances.
22  */
23 public class UnconditionalFlowInfo extends FlowInfo {
24
25         
26         public long definiteInits;
27         public long potentialInits;
28         public long extraDefiniteInits[];
29         public long extraPotentialInits[];
30         
31         public int reachMode = REACHABLE; // by default
32
33         public int maxFieldCount;
34         
35         // Constants
36         public static final int BitCacheSize = 64; // 64 bits in a long.
37
38         UnconditionalFlowInfo() {
39         }
40
41         // unions of both sets of initialization - used for try/finally
42         public FlowInfo addInitializationsFrom(FlowInfo inits) {
43
44                 if (this == DEAD_END)
45                         return this;
46
47                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();  
48                 if (otherInits == DEAD_END)
49                         return this;
50                         
51                 // union of definitely assigned variables, 
52                 definiteInits |= otherInits.definiteInits;
53                 // union of potentially set ones
54                 potentialInits |= otherInits.potentialInits;
55         
56                 // treating extra storage
57                 if (extraDefiniteInits != null) {
58                         if (otherInits.extraDefiniteInits != null) {
59                                 // both sides have extra storage
60                                 int i = 0, length, otherLength;
61                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
62                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
63                                         System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
64                                         System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
65                                         while (i < length) {
66                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
67                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
68                                         }
69                                         while (i < otherLength) {
70                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
71                                         }
72                                 } else {
73                                         // current storage is longer
74                                         while (i < otherLength) {
75                                                 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
76                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
77                                         }
78                                         while (i < length)
79                                                 extraDefiniteInits[i++] = 0;
80                                 }
81                         } else {
82                                 // no extra storage on otherInits
83                         }
84                 } else
85                         if (otherInits.extraDefiniteInits != null) {
86                                 // no storage here, but other has extra storage.
87                                 int otherLength;
88                                 System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength);                        
89                                 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
90                         }
91                 return this;
92         }
93
94         // unions of both sets of initialization - used for try/finally
95         public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
96         
97                 if (this == DEAD_END){
98                         return this;
99                 }
100
101                 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
102                 if (otherInits == DEAD_END){
103                         return this;
104                 }
105                 // union of potentially set ones
106                 potentialInits |= otherInits.potentialInits;
107         
108                 // treating extra storage
109                 if (extraDefiniteInits != null) {
110                         if (otherInits.extraDefiniteInits != null) {
111                                 // both sides have extra storage
112                                 int i = 0, length, otherLength;
113                                 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
114                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
115                                         System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
116                                         System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
117                                         while (i < length) {
118                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
119                                         }
120                                         while (i < otherLength) {
121                                                 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
122                                         }
123                                 } else {
124                                         // current storage is longer
125                                         while (i < otherLength) {
126                                                 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
127                                         }
128                                 }
129                         }
130                 } else
131                         if (otherInits.extraDefiniteInits != null) {
132                                 // no storage here, but other has extra storage.
133                                 int otherLength;
134                                 extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];                      
135                                 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
136                         }
137                 return this;
138         }
139
140         // Report an error if necessary
141 //      public boolean complainIfUnreachable(Statement statement, BlockScope scope, boolean didAlreadyComplain) {
142 //      
143 //              if ((this.reachMode & UNREACHABLE) != 0) {
144 //                      statement.bits &= ~ASTNode.IsReachableMASK;
145 //                      boolean reported = this == DEAD_END;
146 //                      if (!didAlreadyComplain && reported) {
147 //                              scope.problemReporter().unreachableCode(statement);
148 //                      }
149 //                      return reported; // keep going for fake reachable
150 //              }
151 //              return false;
152 //      }
153         
154         /**
155          * Answers a copy of the current instance
156          */
157         public FlowInfo copy() {
158                 
159                 // do not clone the DeadEnd
160                 if (this == DEAD_END)
161                         return this;
162         
163                 // look for an unused preallocated object
164                 UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
165         
166                 // copy slots
167                 copy.definiteInits = this.definiteInits;
168                 copy.potentialInits = this.potentialInits;
169                 copy.reachMode = this.reachMode;
170                 copy.maxFieldCount = this.maxFieldCount;
171                 
172                 if (this.extraDefiniteInits != null) {
173                         int length;
174                         System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[ (length = extraDefiniteInits.length)]), 0, length);
175                         System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length);
176                 };
177                 return copy;
178         }
179         
180         public UnconditionalFlowInfo discardFieldInitializations(){
181                 
182                 int limit = this.maxFieldCount;
183                 
184                 if (limit < BitCacheSize) {
185                         long mask = (1L << limit)-1;
186                         this.definiteInits &= ~mask;
187                         this.potentialInits &= ~mask;
188                         return this;
189                 } 
190
191                 this.definiteInits = 0;
192                 this.potentialInits = 0;
193
194                 // use extra vector
195                 if (extraDefiniteInits == null) {
196                         return this; // if vector not yet allocated, then not initialized
197                 }
198                 int vectorIndex, length = this.extraDefiniteInits.length;
199                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
200                         return this; // not enough room yet
201                 }
202                 for (int i = 0; i < vectorIndex; i++) {
203                         this.extraDefiniteInits[i] = 0L;
204                         this.extraPotentialInits[i] = 0L;
205                 }
206                 long mask = (1L << (limit % BitCacheSize))-1;
207                 this.extraDefiniteInits[vectorIndex] &= ~mask;
208                 this.extraPotentialInits[vectorIndex] &= ~mask;
209                 return this;
210         }
211
212         public UnconditionalFlowInfo discardNonFieldInitializations(){
213                 
214                 int limit = this.maxFieldCount;
215                 
216                 if (limit < BitCacheSize) {
217                         long mask = (1L << limit)-1;
218                         this.definiteInits &= mask;
219                         this.potentialInits &= mask;
220                         return this;
221                 } 
222                 // use extra vector
223                 if (extraDefiniteInits == null) {
224                         return this; // if vector not yet allocated, then not initialized
225                 }
226                 int vectorIndex, length = this.extraDefiniteInits.length;
227                 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
228                         return this; // not enough room yet
229                 }
230                 long mask = (1L << (limit % BitCacheSize))-1;
231                 this.extraDefiniteInits[vectorIndex] &= mask;
232                 this.extraPotentialInits[vectorIndex] &= mask;
233                 for (int i = vectorIndex+1; i < length; i++) {
234                         this.extraDefiniteInits[i] = 0L;
235                         this.extraPotentialInits[i] = 0L;
236                 }
237                 return this;
238         }
239         
240         public FlowInfo initsWhenFalse() {
241                 
242                 return this;
243         }
244         
245         public FlowInfo initsWhenTrue() {
246                 
247                 return this;
248         }
249         
250         /**
251          * Check status of definite assignment at a given position.
252          * It deals with the dual representation of the InitializationInfo2:
253          * bits for the first 64 entries, then an array of booleans.
254          */
255         final private boolean isDefinitelyAssigned(int position) {
256                 
257                 // Dependant of CodeStream.isDefinitelyAssigned(..)
258                 // id is zero-based
259                 if (position < BitCacheSize) {
260                         return (definiteInits & (1L << position)) != 0; // use bits
261                 }
262                 // use extra vector
263                 if (extraDefiniteInits == null)
264                         return false; // if vector not yet allocated, then not initialized
265                 int vectorIndex;
266                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length)
267                         return false; // if not enough room in vector, then not initialized 
268                 return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
269         }
270         
271         /**
272          * Check status of definite assignment for a field.
273          */
274         final public boolean isDefinitelyAssigned(FieldBinding field) {
275                 
276                 // Dependant of CodeStream.isDefinitelyAssigned(..)
277                 // We do not want to complain in unreachable code
278                 if ((this.reachMode & UNREACHABLE) != 0)  
279                         return true;
280                 return isDefinitelyAssigned(field.id); 
281         }
282         
283         /**
284          * Check status of definite assignment for a local.
285          */
286         final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
287                 
288                 // Dependant of CodeStream.isDefinitelyAssigned(..)
289                 // We do not want to complain in unreachable code
290                 if ((this.reachMode & UNREACHABLE) != 0)
291                         return true;
292                 if (local.isArgument) {
293                         return true;
294                 }
295                 // final constants are inlined, and thus considered as always initialized
296                 if (local.constant != Constant.NotAConstant) {
297                         return true;
298                 }
299                 return isDefinitelyAssigned(local.id + maxFieldCount);
300         }
301         
302         public boolean isReachable() {
303                 
304                 return this.reachMode == REACHABLE;
305         }
306         
307         /**
308          * Check status of potential assignment at a given position.
309          * It deals with the dual representation of the InitializationInfo3:
310          * bits for the first 64 entries, then an array of booleans.
311          */
312         final private boolean isPotentiallyAssigned(int position) {
313                 
314                 // id is zero-based
315                 if (position < BitCacheSize) {
316                         // use bits
317                         return (potentialInits & (1L << position)) != 0;
318                 }
319                 // use extra vector
320                 if (extraPotentialInits == null)
321                         return false; // if vector not yet allocated, then not initialized
322                 int vectorIndex;
323                 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length)
324                         return false; // if not enough room in vector, then not initialized 
325                 return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
326         }
327         
328         /**
329          * Check status of definite assignment for a field.
330          */
331         final public boolean isPotentiallyAssigned(FieldBinding field) {
332                 
333                 return isPotentiallyAssigned(field.id); 
334         }
335         
336         /**
337          * Check status of potential assignment for a local.
338          */
339         final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
340                 
341                 if (local.isArgument) {
342                         return true;
343                 }
344                 // final constants are inlined, and thus considered as always initialized
345                 if (local.constant != Constant.NotAConstant) {
346                         return true;
347                 }
348                 return isPotentiallyAssigned(local.id + maxFieldCount);
349         }
350         
351         /**
352          * Record a definite assignment at a given position.
353          * It deals with the dual representation of the InitializationInfo2:
354          * bits for the first 64 entries, then an array of booleans.
355          */
356         final private void markAsDefinitelyAssigned(int position) {
357                 
358                 if (this != DEAD_END) {
359         
360                         // position is zero-based
361                         if (position < BitCacheSize) {
362                                 // use bits
363                                 long mask;
364                                 definiteInits |= (mask = 1L << position);
365                                 potentialInits |= mask;
366                         } else {
367                                 // use extra vector
368                                 int vectorIndex = (position / BitCacheSize) - 1;
369                                 if (extraDefiniteInits == null) {
370                                         int length;
371                                         extraDefiniteInits = new long[length = vectorIndex + 1];
372                                         extraPotentialInits = new long[length];
373                                 } else {
374                                         int oldLength; // might need to grow the arrays
375                                         if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
376                                                 System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength);
377                                                 System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength);
378                                         }
379                                 }
380                                 long mask;
381                                 extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
382                                 extraPotentialInits[vectorIndex] |= mask;
383                         }
384                 }
385         }
386         
387         /**
388          * Record a field got definitely assigned.
389          */
390         public void markAsDefinitelyAssigned(FieldBinding field) {
391                 if (this != DEAD_END)
392                         markAsDefinitelyAssigned(field.id);
393         }
394         
395         /**
396          * Record a local got definitely assigned.
397          */
398         public void markAsDefinitelyAssigned(LocalVariableBinding local) {
399                 if (this != DEAD_END)
400                         markAsDefinitelyAssigned(local.id + maxFieldCount);
401         }
402         
403         /**
404          * Clear initialization information at a given position.
405          * It deals with the dual representation of the InitializationInfo2:
406          * bits for the first 64 entries, then an array of booleans.
407          */
408         final private void markAsDefinitelyNotAssigned(int position) {
409                 if (this != DEAD_END) {
410         
411                         // position is zero-based
412                         if (position < BitCacheSize) {
413                                 // use bits
414                                 long mask;
415                                 definiteInits &= ~(mask = 1L << position);
416                                 potentialInits &= ~mask;
417                         } else {
418                                 // use extra vector
419                                 int vectorIndex = (position / BitCacheSize) - 1;
420                                 if (extraDefiniteInits == null) {
421                                         return; // nothing to do, it was not yet set 
422                                 } else {
423                                         // might need to grow the arrays
424                                         if (vectorIndex >= extraDefiniteInits.length) {
425                                                 return; // nothing to do, it was not yet set 
426                                         }
427                                 }
428                                 long mask;
429                                 extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
430                                 extraPotentialInits[vectorIndex] &= ~mask;
431                         }
432                 }
433         }
434         
435         /**
436          * Clear the initialization info for a field
437          */
438         public void markAsDefinitelyNotAssigned(FieldBinding field) {
439                 
440                 if (this != DEAD_END)
441                         markAsDefinitelyNotAssigned(field.id);
442         }
443         
444         /**
445          * Clear the initialization info for a local variable
446          */
447         
448         public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
449                 
450                 if (this != DEAD_END)
451                         markAsDefinitelyNotAssigned(local.id + maxFieldCount);
452         }
453                 
454         /**
455          * Returns the receiver updated in the following way: <ul>
456          * <li> intersection of definitely assigned variables, 
457          * <li> union of potentially assigned variables.
458          * </ul>
459          */
460         public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
461         
462                 if (this == DEAD_END) return otherInits;
463                 if (otherInits == DEAD_END) return this;
464         
465                 if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){
466                         if ((this.reachMode & UNREACHABLE) != 0){
467                                 return otherInits;
468                         } else {
469                                 return this;
470                         }
471                 }
472                 
473                 // if one branch is not fake reachable, then the merged one is reachable
474                 this.reachMode &= otherInits.reachMode;
475         
476                 // intersection of definitely assigned variables, 
477                 this.definiteInits &= otherInits.definiteInits;
478                 // union of potentially set ones
479                 this.potentialInits |= otherInits.potentialInits;
480         
481                 // treating extra storage
482                 if (this.extraDefiniteInits != null) {
483                         if (otherInits.extraDefiniteInits != null) {
484                                 // both sides have extra storage
485                                 int i = 0, length, otherLength;
486                                 if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
487                                         // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
488                                         System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
489                                         System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
490                                         while (i < length) {
491                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
492                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
493                                         }
494                                         while (i < otherLength) {
495                                                 this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
496                                         }
497                                 } else {
498                                         // current storage is longer
499                                         while (i < otherLength) {
500                                                 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
501                                                 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
502                                         }
503                                         while (i < length)
504                                                 this.extraDefiniteInits[i++] = 0;
505                                 }
506                         } else {
507                                 // no extra storage on otherInits
508                                 int i = 0, length = this.extraDefiniteInits.length;
509                                 while (i < length)
510                                         this.extraDefiniteInits[i++] = 0;
511                         }
512                 } else
513                         if (otherInits.extraDefiniteInits != null) {
514                                 // no storage here, but other has extra storage.
515                                 int otherLength;
516                                 this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
517                                 System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
518                         }
519                 return this;
520         }
521         
522         /*
523          * Answer the total number of fields in enclosing types of a given type
524          */
525         static int numberOfEnclosingFields(ReferenceBinding type){
526                 
527                 int count = 0;
528                 type = type.enclosingType();
529                 while(type != null) {
530                         count += type.fieldCount();
531                         type = type.enclosingType();
532                 }
533                 return count;
534         }
535         
536         public int reachMode(){
537                 return this.reachMode;
538         }
539         
540         public FlowInfo setReachMode(int reachMode) {
541                 
542                 if (this == DEAD_END) return this; // cannot modify DEAD_END
543         
544                 // reset optional inits when becoming unreachable
545                 if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) {
546                         this.potentialInits = 0;
547                         if (this.extraPotentialInits != null){
548                                 for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){
549                                         this.extraPotentialInits[i] = 0;
550                                 }
551                         }
552                 }                               
553                 this.reachMode = reachMode;
554         
555                 return this;
556         }
557
558         public String toString(){
559                 
560                 if (this == DEAD_END){
561                         return "FlowInfo.DEAD_END"; //$NON-NLS-1$
562                 }
563                 return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$
564                         +", pot: " + this.potentialInits  //$NON-NLS-1$
565                         + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
566                         +">"; //$NON-NLS-1$
567         }
568         
569         public UnconditionalFlowInfo unconditionalInits() {
570                 
571                 // also see conditional inits, where it requests them to merge
572                 return this;
573         }
574 }