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