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