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