1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.flow;
13 import org.eclipse.jdt.internal.compiler.impl.Constant;
14 import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
15 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
16 import org.eclipse.jdt.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; // by default
33 public int maxFieldCount;
36 public static final int BitCacheSize = 64; // 64 bits in a long.
38 UnconditionalFlowInfo() {
39 this.reachMode = REACHABLE;
42 // unions of both sets of initialization - used for try/finally
43 public FlowInfo addInitializationsFrom(FlowInfo inits) {
48 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
49 if (otherInits == DEAD_END)
52 // union of definitely assigned variables,
53 definiteInits |= otherInits.definiteInits;
54 // union of potentially set ones
55 potentialInits |= otherInits.potentialInits;
57 // treating extra storage
58 if (extraDefiniteInits != null) {
59 if (otherInits.extraDefiniteInits != null) {
60 // both sides have extra storage
61 int i = 0, length, otherLength;
62 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
63 // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
64 System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
65 System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
67 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
68 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
70 while (i < otherLength) {
71 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
74 // current storage is longer
75 while (i < otherLength) {
76 extraDefiniteInits[i] |= otherInits.extraDefiniteInits[i];
77 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
80 extraDefiniteInits[i++] = 0;
83 // no extra storage on otherInits
86 if (otherInits.extraDefiniteInits != null) {
87 // no storage here, but other has extra storage.
89 System.arraycopy(otherInits.extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length]), 0, otherLength);
90 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
95 // unions of both sets of initialization - used for try/finally
96 public FlowInfo addPotentialInitializationsFrom(FlowInfo inits) {
98 if (this == DEAD_END){
102 UnconditionalFlowInfo otherInits = inits.unconditionalInits();
103 if (otherInits == DEAD_END){
106 // union of potentially set ones
107 potentialInits |= otherInits.potentialInits;
109 // treating extra storage
110 if (extraDefiniteInits != null) {
111 if (otherInits.extraDefiniteInits != null) {
112 // both sides have extra storage
113 int i = 0, length, otherLength;
114 if ((length = extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
115 // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
116 System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[otherLength]), 0, length);
117 System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, length);
119 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
121 while (i < otherLength) {
122 extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
125 // current storage is longer
126 while (i < otherLength) {
127 extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
132 if (otherInits.extraDefiniteInits != null) {
133 // no storage here, but other has extra storage.
135 extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
136 System.arraycopy(otherInits.extraPotentialInits, 0, (extraPotentialInits = new long[otherLength]), 0, otherLength);
142 * Answers a copy of the current instance
144 public FlowInfo copy() {
146 // do not clone the DeadEnd
147 if (this == DEAD_END)
150 // look for an unused preallocated object
151 UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
154 copy.definiteInits = this.definiteInits;
155 copy.potentialInits = this.potentialInits;
156 copy.reachMode = this.reachMode;
157 copy.maxFieldCount = this.maxFieldCount;
159 if (this.extraDefiniteInits != null) {
161 System.arraycopy(this.extraDefiniteInits, 0, (copy.extraDefiniteInits = new long[ (length = extraDefiniteInits.length)]), 0, length);
162 System.arraycopy(this.extraPotentialInits, 0, (copy.extraPotentialInits = new long[length]), 0, length);
167 public UnconditionalFlowInfo discardFieldInitializations(){
169 int limit = this.maxFieldCount;
171 if (limit < BitCacheSize) {
172 long mask = (1L << limit)-1;
173 this.definiteInits &= ~mask;
174 this.potentialInits &= ~mask;
178 this.definiteInits = 0;
179 this.potentialInits = 0;
182 if (extraDefiniteInits == null) {
183 return this; // if vector not yet allocated, then not initialized
185 int vectorIndex, length = this.extraDefiniteInits.length;
186 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
187 return this; // not enough room yet
189 for (int i = 0; i < vectorIndex; i++) {
190 this.extraDefiniteInits[i] = 0L;
191 this.extraPotentialInits[i] = 0L;
193 long mask = (1L << (limit % BitCacheSize))-1;
194 this.extraDefiniteInits[vectorIndex] &= ~mask;
195 this.extraPotentialInits[vectorIndex] &= ~mask;
199 public UnconditionalFlowInfo discardNonFieldInitializations(){
201 int limit = this.maxFieldCount;
203 if (limit < BitCacheSize) {
204 long mask = (1L << limit)-1;
205 this.definiteInits &= mask;
206 this.potentialInits &= mask;
210 if (extraDefiniteInits == null) {
211 return this; // if vector not yet allocated, then not initialized
213 int vectorIndex, length = this.extraDefiniteInits.length;
214 if ((vectorIndex = (limit / BitCacheSize) - 1) >= length) {
215 return this; // not enough room yet
217 long mask = (1L << (limit % BitCacheSize))-1;
218 this.extraDefiniteInits[vectorIndex] &= mask;
219 this.extraPotentialInits[vectorIndex] &= mask;
220 for (int i = vectorIndex+1; i < length; i++) {
221 this.extraDefiniteInits[i] = 0L;
222 this.extraPotentialInits[i] = 0L;
227 public FlowInfo initsWhenFalse() {
232 public FlowInfo initsWhenTrue() {
238 * Check status of definite assignment at a given position.
239 * It deals with the dual representation of the InitializationInfo2:
240 * bits for the first 64 entries, then an array of booleans.
242 final private boolean isDefinitelyAssigned(int position) {
244 // Dependant of CodeStream.isDefinitelyAssigned(..)
246 if (position < BitCacheSize) {
247 return (definiteInits & (1L << position)) != 0; // use bits
250 if (extraDefiniteInits == null)
251 return false; // if vector not yet allocated, then not initialized
253 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraDefiniteInits.length)
254 return false; // if not enough room in vector, then not initialized
255 return ((extraDefiniteInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
259 * Check status of definite assignment for a field.
261 final public boolean isDefinitelyAssigned(FieldBinding field) {
263 // Dependant of CodeStream.isDefinitelyAssigned(..)
264 // We do not want to complain in unreachable code
265 if ((this.reachMode & UNREACHABLE) != 0)
267 return isDefinitelyAssigned(field.id);
271 * Check status of definite assignment for a local.
273 final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
275 // Dependant of CodeStream.isDefinitelyAssigned(..)
276 // We do not want to complain in unreachable code
277 if ((this.reachMode & UNREACHABLE) != 0)
279 if (local.isArgument) {
282 // final constants are inlined, and thus considered as always initialized
283 if (local.constant != Constant.NotAConstant) {
286 return isDefinitelyAssigned(local.id + maxFieldCount);
289 public boolean isReachable() {
291 return this.reachMode == REACHABLE;
295 * Check status of potential assignment at a given position.
296 * It deals with the dual representation of the InitializationInfo3:
297 * bits for the first 64 entries, then an array of booleans.
299 final private boolean isPotentiallyAssigned(int position) {
302 if (position < BitCacheSize) {
304 return (potentialInits & (1L << position)) != 0;
307 if (extraPotentialInits == null)
308 return false; // if vector not yet allocated, then not initialized
310 if ((vectorIndex = (position / BitCacheSize) - 1) >= extraPotentialInits.length)
311 return false; // if not enough room in vector, then not initialized
312 return ((extraPotentialInits[vectorIndex]) & (1L << (position % BitCacheSize))) != 0;
316 * Check status of definite assignment for a field.
318 final public boolean isPotentiallyAssigned(FieldBinding field) {
320 return isPotentiallyAssigned(field.id);
324 * Check status of potential assignment for a local.
326 final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
328 if (local.isArgument) {
331 // final constants are inlined, and thus considered as always initialized
332 if (local.constant != Constant.NotAConstant) {
335 return isPotentiallyAssigned(local.id + maxFieldCount);
339 * Record a definite assignment at a given position.
340 * It deals with the dual representation of the InitializationInfo2:
341 * bits for the first 64 entries, then an array of booleans.
343 final private void markAsDefinitelyAssigned(int position) {
345 if (this != DEAD_END) {
347 // position is zero-based
348 if (position < BitCacheSize) {
351 definiteInits |= (mask = 1L << position);
352 potentialInits |= mask;
355 int vectorIndex = (position / BitCacheSize) - 1;
356 if (extraDefiniteInits == null) {
358 extraDefiniteInits = new long[length = vectorIndex + 1];
359 extraPotentialInits = new long[length];
361 int oldLength; // might need to grow the arrays
362 if (vectorIndex >= (oldLength = extraDefiniteInits.length)) {
363 System.arraycopy(extraDefiniteInits, 0, (extraDefiniteInits = new long[vectorIndex + 1]), 0, oldLength);
364 System.arraycopy(extraPotentialInits, 0, (extraPotentialInits = new long[vectorIndex + 1]), 0, oldLength);
368 extraDefiniteInits[vectorIndex] |= (mask = 1L << (position % BitCacheSize));
369 extraPotentialInits[vectorIndex] |= mask;
375 * Record a field got definitely assigned.
377 public void markAsDefinitelyAssigned(FieldBinding field) {
378 if (this != DEAD_END)
379 markAsDefinitelyAssigned(field.id);
383 * Record a local got definitely assigned.
385 public void markAsDefinitelyAssigned(LocalVariableBinding local) {
386 if (this != DEAD_END)
387 markAsDefinitelyAssigned(local.id + maxFieldCount);
391 * Clear initialization information at a given position.
392 * It deals with the dual representation of the InitializationInfo2:
393 * bits for the first 64 entries, then an array of booleans.
395 final private void markAsDefinitelyNotAssigned(int position) {
396 if (this != DEAD_END) {
398 // position is zero-based
399 if (position < BitCacheSize) {
402 definiteInits &= ~(mask = 1L << position);
403 potentialInits &= ~mask;
406 int vectorIndex = (position / BitCacheSize) - 1;
407 if (extraDefiniteInits == null) {
408 return; // nothing to do, it was not yet set
410 // might need to grow the arrays
411 if (vectorIndex >= extraDefiniteInits.length) {
412 return; // nothing to do, it was not yet set
415 extraDefiniteInits[vectorIndex] &= ~(mask = 1L << (position % BitCacheSize));
416 extraPotentialInits[vectorIndex] &= ~mask;
422 * Clear the initialization info for a field
424 public void markAsDefinitelyNotAssigned(FieldBinding field) {
426 if (this != DEAD_END)
427 markAsDefinitelyNotAssigned(field.id);
431 * Clear the initialization info for a local variable
434 public void markAsDefinitelyNotAssigned(LocalVariableBinding local) {
436 if (this != DEAD_END)
437 markAsDefinitelyNotAssigned(local.id + maxFieldCount);
441 * Returns the receiver updated in the following way: <ul>
442 * <li> intersection of definitely assigned variables,
443 * <li> union of potentially assigned variables.
446 public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) {
448 if (this == DEAD_END) return otherInits;
449 if (otherInits == DEAD_END) return this;
451 if ((this.reachMode & UNREACHABLE) != (otherInits.reachMode & UNREACHABLE)){
452 if ((this.reachMode & UNREACHABLE) != 0){
458 // if one branch is not fake reachable, then the merged one is reachable
459 this.reachMode &= otherInits.reachMode;
461 // intersection of definitely assigned variables,
462 this.definiteInits &= otherInits.definiteInits;
463 // union of potentially set ones
464 this.potentialInits |= otherInits.potentialInits;
466 // treating extra storage
467 if (this.extraDefiniteInits != null) {
468 if (otherInits.extraDefiniteInits != null) {
469 // both sides have extra storage
470 int i = 0, length, otherLength;
471 if ((length = this.extraDefiniteInits.length) < (otherLength = otherInits.extraDefiniteInits.length)) {
472 // current storage is shorter -> grow current (could maybe reuse otherInits extra storage?)
473 System.arraycopy(this.extraDefiniteInits, 0, (this.extraDefiniteInits = new long[otherLength]), 0, length);
474 System.arraycopy(this.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, length);
476 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
477 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
479 while (i < otherLength) {
480 this.extraPotentialInits[i] = otherInits.extraPotentialInits[i++];
483 // current storage is longer
484 while (i < otherLength) {
485 this.extraDefiniteInits[i] &= otherInits.extraDefiniteInits[i];
486 this.extraPotentialInits[i] |= otherInits.extraPotentialInits[i++];
489 this.extraDefiniteInits[i++] = 0;
492 // no extra storage on otherInits
493 int i = 0, length = this.extraDefiniteInits.length;
495 this.extraDefiniteInits[i++] = 0;
498 if (otherInits.extraDefiniteInits != null) {
499 // no storage here, but other has extra storage.
501 this.extraDefiniteInits = new long[otherLength = otherInits.extraDefiniteInits.length];
502 System.arraycopy(otherInits.extraPotentialInits, 0, (this.extraPotentialInits = new long[otherLength]), 0, otherLength);
508 * Answer the total number of fields in enclosing types of a given type
510 static int numberOfEnclosingFields(ReferenceBinding type){
513 type = type.enclosingType();
514 while(type != null) {
515 count += type.fieldCount();
516 type = type.enclosingType();
521 public int reachMode(){
522 return this.reachMode;
525 public FlowInfo setReachMode(int reachMode) {
527 if (this == DEAD_END) return this; // cannot modify DEAD_END
529 // reset optional inits when becoming unreachable
530 if ((this.reachMode & UNREACHABLE) == 0 && (reachMode & UNREACHABLE) != 0) {
531 this.potentialInits = 0;
532 if (this.extraPotentialInits != null){
533 for (int i = 0, length = this.extraPotentialInits.length; i < length; i++){
534 this.extraPotentialInits[i] = 0;
538 this.reachMode = reachMode;
543 public String toString(){
545 if (this == DEAD_END){
546 return "FlowInfo.DEAD_END"; //$NON-NLS-1$
548 return "FlowInfo<def: "+ this.definiteInits //$NON-NLS-1$
549 +", pot: " + this.potentialInits //$NON-NLS-1$
550 + ", reachable:" + ((this.reachMode & UNREACHABLE) == 0) //$NON-NLS-1$
554 public UnconditionalFlowInfo unconditionalInits() {
556 // also see conditional inits, where it requests them to merge