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.FieldBinding;
15 import net.sourceforge.phpdt.internal.compiler.lookup.LocalVariableBinding;
16 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
19 * Record initialization status during definite assignment analysis
21 * No caching of pre-allocated instances.
23 public class UnconditionalFlowInfo extends FlowInfo {
26 public long definiteInits;
27 public long potentialInits;
28 public long extraDefiniteInits[];
29 public long extraPotentialInits[];
31 public int reachMode = REACHABLE; // by default
33 public int maxFieldCount;
36 public static final int BitCacheSize = 64; // 64 bits in a long.
38 UnconditionalFlowInfo() {
41 // unions of both sets of initialization - used for try/finally
42 public FlowInfo addInitializationsFrom(FlowInfo inits) {
47 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
48 if (otherInits == DEAD_END)
51 // union of definitely assigned variables,
52 definiteInits |= otherInits.definiteInits;
53 // union of potentially set ones
54 potentialInits |= otherInits.potentialInits;
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);
66 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
67 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
69 while (i < otherLength) {
70 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
73 // current storage is longer
74 while (i < otherLength) {
75 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
76 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
79 extraDefiniteInits[i++] = 0;
82 // no extra storage on otherInits
85 if (otherInits.extraDefiniteInits != null) {
86 // no storage here, but other has extra storage.
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);
94 // unions of both sets of initialization - used for try/finally
95 public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
97 if (this == DEAD_END){
101 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
102 if (otherInits == DEAD_END){
105 // union of potentially set ones
106 potentialInits |= otherInits.potentialInits;
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);
118 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
120 while (i < otherLength) {
121 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
124 // current storage is longer
125 while (i < otherLength) {
126 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
131 if (otherInits.extraDefiniteInits != null) {
132 // no storage here, but other has extra storage.
134 extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
135 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
140 // Report an error if necessary
141 // public boolean complainIfUnreachable(Statement statement, BlockScope scope, boolean didAlreadyComplain) {
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);
149 // return reported; // keep going for fake reachable
155 * Answers a copy of the current instance
157 public FlowInfo copy() {
159 // do not clone the DeadEnd
160 if (this == DEAD_END)
163 // look for an unused preallocated object
164 UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
167 copy.definiteInits = this.definiteInits;
168 copy.potentialInits = this.potentialInits;
169 copy.reachMode = this.reachMode;
170 copy.maxFieldCount = this.maxFieldCount;
172 if (this.extraDefiniteInits != null) {
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);
180 public UnconditionalFlowInfo discardFieldInitializations(){
182 int limit = this.maxFieldCount;
184 if (limit < BitCacheSize) {
185 long mask = (1L << limit)-1;
186 this.definiteInits &= ~mask;
187 this.potentialInits &= ~mask;
191 this.definiteInits = 0;
192 this.potentialInits = 0;
195 if (extraDefiniteInits == null) {
196 return this; // if vector not yet allocated, then not initialized
198 int vectorIndex, length = this.extraDefiniteInits.length;
199 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
200 return this; // not enough room yet
202 for (int i = 0; i < vectorIndex; i++) {
203 this.extraDefiniteInits[i] = 0L;
204 this.extraPotentialInits[i] = 0L;
206 long mask = (1L << (limit % BitCacheSize))-1;
207 this.extraDefiniteInits[vectorIndex] &= ~mask;
208 this.extraPotentialInits[vectorIndex] &= ~mask;
212 public UnconditionalFlowInfo discardNonFieldInitializations(){
214 int limit = this.maxFieldCount;
216 if (limit < BitCacheSize) {
217 long mask = (1L << limit)-1;
218 this.definiteInits &= mask;
219 this.potentialInits &= mask;
223 if (extraDefiniteInits == null) {
224 return this; // if vector not yet allocated, then not initialized
226 int vectorIndex, length = this.extraDefiniteInits.length;
227 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
228 return this; // not enough room yet
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;
240 public FlowInfo initsWhenFalse() {
245 public FlowInfo initsWhenTrue() {
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.
255 final private boolean isDefinitelyAssigned(int position) {
257 // Dependant of CodeStream.isDefinitelyAssigned(..)
259 if (position < BitCacheSize) {
260 return (definiteInits & (1L << position)) != 0; // use bits
263 if (extraDefiniteInits == null)
264 return false; // if vector not yet allocated, then not initialized
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;
272 * Check status of definite assignment for a field.
274 final public boolean isDefinitelyAssigned(FieldBinding field) {
276 // Dependant of CodeStream.isDefinitelyAssigned(..)
277 // We do not want to complain in unreachable code
278 if ((this.reachMode & UNREACHABLE) != 0)
280 return isDefinitelyAssigned(field.id);
284 * Check status of definite assignment for a local.
286 final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
288 // Dependant of CodeStream.isDefinitelyAssigned(..)
289 // We do not want to complain in unreachable code
290 if ((this.reachMode & UNREACHABLE) != 0)
292 if (local.isArgument) {
295 // final constants are inlined, and thus considered as always initialized
296 if (local.constant != Constant.NotAConstant) {
299 return isDefinitelyAssigned(local.id + maxFieldCount);
302 public boolean isReachable() {
304 return this.reachMode == REACHABLE;
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.
312 final private boolean isPotentiallyAssigned(int position) {
315 if (position < BitCacheSize) {
317 return (potentialInits & (1L << position)) != 0;
320 if (extraPotentialInits == null)
321 return false; // if vector not yet allocated, then not initialized
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;
329 * Check status of definite assignment for a field.
331 final public boolean isPotentiallyAssigned(FieldBinding field) {
333 return isPotentiallyAssigned(field.id);
337 * Check status of potential assignment for a local.
339 final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
341 if (local.isArgument) {
344 // final constants are inlined, and thus considered as always initialized
345 if (local.constant != Constant.NotAConstant) {
348 return isPotentiallyAssigned(local.id + maxFieldCount);
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.
356 final private void markAsDefinitelyAssigned(int position) {
358 if (this != DEAD_END) {
360 // position is zero-based
361 if (position < BitCacheSize) {
364 definiteInits |= (mask = 1L << position);
365 potentialInits |= mask;
368 int vectorIndex = (position / BitCacheSize) - 1;
369 if (extraDefiniteInits == null) {
371 extraDefiniteInits = new long[length = vectorIndex + 1];
372 extraPotentialInits = new long[length];
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);
381 extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
382 extraPotentialInits[vectorIndex] |= mask;
388 * Record a field got definitely assigned.
390 public void markAsDefinitelyAssigned(FieldBinding field) {
391 if (this != DEAD_END)
392 markAsDefinitelyAssigned(field.id);
396 * Record a local got definitely assigned.
398 public void markAsDefinitelyAssigned(LocalVariableBinding local) {
399 if (this != DEAD_END)
400 markAsDefinitelyAssigned(local.id + maxFieldCount);
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.
408 final private void markAsDefinitelyNotAssigned(int position) {
409 if (this != DEAD_END) {
411 // position is zero-based
412 if (position < BitCacheSize) {
415 definiteInits &= ~(mask = 1L << position);
416 potentialInits &= ~mask;
419 int vectorIndex = (position / BitCacheSize) - 1;
420 if (extraDefiniteInits == null) {
421 return; // nothing to do, it was not yet set
423 // might need to grow the arrays
424 if (vectorIndex >= extraDefiniteInits.length) {
425 return; // nothing to do, it was not yet set
429 extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
430 extraPotentialInits[vectorIndex] &= ~mask;
436 * Clear the initialization info for a field
438 public void markAsDefinitelyNotAssigned(FieldBinding field) {
440 if (this != DEAD_END)
441 markAsDefinitelyNotAssigned(field.id);
445 * Clear the initialization info for a local variable
448 public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
450 if (this != DEAD_END)
451 markAsDefinitelyNotAssigned(local.id + maxFieldCount);
455 * Returns the receiver updated in the following way: <ul>
456 * <li> intersection of definitely assigned variables,
457 * <li> union of potentially assigned variables.
460 public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
462 if (this == DEAD_END) return otherInits;
463 if (otherInits == DEAD_END) return this;
465 if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){
466 if ((this.reachMode & UNREACHABLE) != 0){
473 // if one branch is not fake reachable, then the merged one is reachable
474 this.reachMode &= otherInits.reachMode;
476 // intersection of definitely assigned variables,
477 this.definiteInits &= otherInits.definiteInits;
478 // union of potentially set ones
479 this.potentialInits |= otherInits.potentialInits;
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);
491 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
492 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
494 while (i < otherLength) {
495 this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
498 // current storage is longer
499 while (i < otherLength) {
500 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
501 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
504 this.extraDefiniteInits[i++] = 0;
507 // no extra storage on otherInits
508 int i = 0, length = this.extraDefiniteInits.length;
510 this.extraDefiniteInits[i++] = 0;
513 if (otherInits.extraDefiniteInits != null) {
514 // no storage here, but other has extra storage.
516 this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
517 System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
523 * Answer the total number of fields in enclosing types of a given type
525 static int numberOfEnclosingFields(ReferenceBinding type){
528 type = type.enclosingType();
529 while(type != null) {
530 count += type.fieldCount();
531 type = type.enclosingType();
536 public int reachMode(){
537 return this.reachMode;
540 public FlowInfo setReachMode(int reachMode) {
542 if (this == DEAD_END) return this; // cannot modify DEAD_END
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;
553 this.reachMode = reachMode;
558 public String toString(){
560 if (this == DEAD_END){
561 return "FlowInfo.DEAD_END"; //$NON-NLS-1$
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$
569 public UnconditionalFlowInfo unconditionalInits() {
571 // also see conditional inits, where it requests them to merge