001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.flow;
011:
012: import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
013:
014: /**
015: * A degenerate form of UnconditionalFlowInfo explicitly meant to capture
016: * the effects of null related operations within try blocks. Given the fact
017: * that a try block might exit at any time, a null related operation that
018: * occurs within such a block mitigates whatever we know about the previous
019: * null status of involved variables. NullInfoRegistry handles that
020: * by negating upstream definite information that clashes with what a given
021: * statement contends about the same variable. It also implements
022: * {@link #mitigateNullInfoOf(FlowInfo) mitigateNullInfo} so as to elaborate the
023: * flow info presented in input of finally blocks.
024: */
025: public class NullInfoRegistry extends UnconditionalFlowInfo {
026: // significant states at this level:
027: // def. non null, def. null, def. unknown, prot. non null
028:
029: // PREMATURE implement coverage and low level tests
030:
031: /**
032: * Make a new null info registry, using an upstream flow info. All definite
033: * assignments of the upstream are carried forward, since a try block may
034: * exit before its first statement.
035: * @param upstream - UnconditionalFlowInfo: the flow info before we enter the
036: * try block; only definite assignments are considered; this parameter is
037: * not modified by this constructor
038: */
039: public NullInfoRegistry(UnconditionalFlowInfo upstream) {
040: this .maxFieldCount = upstream.maxFieldCount;
041: if ((upstream.tagBits & NULL_FLAG_MASK) != 0) {
042: long u1, u2, u3, u4, nu2, nu3, nu4;
043: this .nullBit2 = (u1 = upstream.nullBit1)
044: & (u2 = upstream.nullBit2)
045: & (nu3 = ~(u3 = upstream.nullBit3))
046: & (nu4 = ~(u4 = upstream.nullBit4));
047: this .nullBit3 = u1 & (nu2 = ~u2) & u3 & nu4;
048: this .nullBit4 = u1 & nu2 & nu3 & u4;
049: if ((this .nullBit2 | this .nullBit3 | this .nullBit4) != 0) {
050: this .tagBits |= NULL_FLAG_MASK;
051: }
052: if (upstream.extra != null) {
053: this .extra = new long[extraLength][];
054: int length = upstream.extra[2].length;
055: for (int i = 2; i < extraLength; i++) {
056: this .extra[i] = new long[length];
057: }
058: for (int i = 0; i < length; i++) {
059: this .extra[2 + 1][i] = (u1 = upstream.extra[1 + 1][i])
060: & (u2 = upstream.extra[2 + 1][i])
061: & (nu3 = ~(u3 = upstream.extra[3 + 1][i]))
062: & (nu4 = ~(u4 = upstream.extra[4 + 1][i]));
063: this .extra[3 + 1][i] = u1 & (nu2 = ~u2) & u3 & nu4;
064: this .extra[4 + 1][i] = u1 & nu2 & nu3 & u4;
065: if ((this .extra[2 + 1][i] | this .extra[3 + 1][i] | this .extra[4 + 1][i]) != 0) {
066: this .tagBits |= NULL_FLAG_MASK;
067: }
068: }
069: }
070: }
071: }
072:
073: /**
074: * Add the information held by another NullInfoRegistry instance to this,
075: * then return this.
076: * @param other - NullInfoRegistry: the information to add to this
077: * @return this, modified to carry the information held by other
078: */
079: public NullInfoRegistry add(NullInfoRegistry other) {
080: if ((other.tagBits & NULL_FLAG_MASK) == 0) {
081: return this ;
082: }
083: this .tagBits |= NULL_FLAG_MASK;
084: this .nullBit1 |= other.nullBit1;
085: this .nullBit2 |= other.nullBit2;
086: this .nullBit3 |= other.nullBit3;
087: this .nullBit4 |= other.nullBit4;
088: if (other.extra != null) {
089: if (this .extra == null) {
090: this .extra = new long[extraLength][];
091: for (int i = 2, length = other.extra[2].length; i < extraLength; i++) {
092: System.arraycopy(other.extra[i], 0,
093: (this .extra[i] = new long[length]), 0,
094: length);
095: }
096: } else {
097: int length = this .extra[2].length, otherLength = other.extra[2].length;
098: if (otherLength > length) {
099: for (int i = 2; i < extraLength; i++) {
100: System
101: .arraycopy(
102: this .extra[i],
103: 0,
104: (this .extra[i] = new long[otherLength]),
105: 0, length);
106: System.arraycopy(other.extra[i], length,
107: this .extra[i], length, otherLength
108: - length);
109: }
110: } else if (otherLength < length) {
111: length = otherLength;
112: }
113: for (int i = 2; i < extraLength; i++) {
114: for (int j = 0; j < length; j++) {
115: this .extra[i][j] |= other.extra[i][j];
116: }
117: }
118: }
119: }
120: return this ;
121: }
122:
123: public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
124: // protected from non-object locals in calling methods
125: if (this != DEAD_END) {
126: this .tagBits |= NULL_FLAG_MASK;
127: int position;
128: // position is zero-based
129: if ((position = local.id + this .maxFieldCount) < BitCacheSize) { // use bits
130: // set protected non null
131: this .nullBit1 |= (1L << position);
132: if (coverageTestFlag && coverageTestId == 290) {
133: this .nullBit1 = 0;
134: }
135: } else {
136: // use extra vector
137: int vectorIndex = (position / BitCacheSize) - 1;
138: if (this .extra == null) {
139: int length = vectorIndex + 1;
140: this .extra = new long[extraLength][];
141: for (int j = 2; j < extraLength; j++) {
142: this .extra[j] = new long[length];
143: }
144: } else {
145: int oldLength; // might need to grow the arrays
146: if (vectorIndex >= (oldLength = this .extra[2].length)) {
147: for (int j = 2; j < extraLength; j++) {
148: System
149: .arraycopy(
150: this .extra[j],
151: 0,
152: (this .extra[j] = new long[vectorIndex + 1]),
153: 0, oldLength);
154: }
155: }
156: }
157: this .extra[2][vectorIndex] |= (1L << (position % BitCacheSize));
158: if (coverageTestFlag && coverageTestId == 300) {
159: this .extra[5][vectorIndex] = ~0;
160: }
161: }
162: }
163: }
164:
165: public void markAsDefinitelyNonNull(LocalVariableBinding local) {
166: // protected from non-object locals in calling methods
167: if (this != DEAD_END) {
168: this .tagBits |= NULL_FLAG_MASK;
169: int position;
170: // position is zero-based
171: if ((position = local.id + this .maxFieldCount) < BitCacheSize) { // use bits
172: // set assigned non null
173: this .nullBit3 |= (1L << position);
174: if (coverageTestFlag && coverageTestId == 290) {
175: this .nullBit1 = 0;
176: }
177: } else {
178: // use extra vector
179: int vectorIndex = (position / BitCacheSize) - 1;
180: if (this .extra == null) {
181: int length = vectorIndex + 1;
182: this .extra = new long[extraLength][];
183: for (int j = 2; j < extraLength; j++) {
184: this .extra[j] = new long[length];
185: }
186: } else {
187: int oldLength; // might need to grow the arrays
188: if (vectorIndex >= (oldLength = this .extra[2].length)) {
189: for (int j = 2; j < extraLength; j++) {
190: System
191: .arraycopy(
192: this .extra[j],
193: 0,
194: (this .extra[j] = new long[vectorIndex + 1]),
195: 0, oldLength);
196: }
197: }
198: }
199: this .extra[4][vectorIndex] |= (1L << (position % BitCacheSize));
200: if (coverageTestFlag && coverageTestId == 300) {
201: this .extra[5][vectorIndex] = ~0;
202: }
203: }
204: }
205: }
206:
207: // PREMATURE consider ignoring extra 0 to 2 included - means a1 should not be used either
208: // PREMATURE project protected non null onto something else
209: public void markAsDefinitelyNull(LocalVariableBinding local) {
210: // protected from non-object locals in calling methods
211: if (this != DEAD_END) {
212: this .tagBits |= NULL_FLAG_MASK;
213: int position;
214: // position is zero-based
215: if ((position = local.id + this .maxFieldCount) < BitCacheSize) { // use bits
216: // set assigned null
217: this .nullBit2 |= (1L << position);
218: if (coverageTestFlag && coverageTestId == 290) {
219: this .nullBit1 = 0;
220: }
221: } else {
222: // use extra vector
223: int vectorIndex = (position / BitCacheSize) - 1;
224: if (this .extra == null) {
225: int length = vectorIndex + 1;
226: this .extra = new long[extraLength][];
227: for (int j = 2; j < extraLength; j++) {
228: this .extra[j] = new long[length];
229: }
230: } else {
231: int oldLength; // might need to grow the arrays
232: if (vectorIndex >= (oldLength = this .extra[2].length)) {
233: for (int j = 2; j < extraLength; j++) {
234: System
235: .arraycopy(
236: this .extra[j],
237: 0,
238: (this .extra[j] = new long[vectorIndex + 1]),
239: 0, oldLength);
240: }
241: }
242: }
243: this .extra[3][vectorIndex] |= (1L << (position % BitCacheSize));
244: if (coverageTestFlag && coverageTestId == 300) {
245: this .extra[5][vectorIndex] = ~0;
246: }
247: }
248: }
249: }
250:
251: public void markAsDefinitelyUnknown(LocalVariableBinding local) {
252: // protected from non-object locals in calling methods
253: if (this != DEAD_END) {
254: this .tagBits |= NULL_FLAG_MASK;
255: int position;
256: // position is zero-based
257: if ((position = local.id + this .maxFieldCount) < BitCacheSize) { // use bits
258: // set assigned unknown
259: this .nullBit4 |= (1L << position);
260: if (coverageTestFlag && coverageTestId == 290) {
261: this .nullBit1 = 0;
262: }
263: } else {
264: // use extra vector
265: int vectorIndex = (position / BitCacheSize) - 1;
266: if (this .extra == null) {
267: int length = vectorIndex + 1;
268: this .extra = new long[extraLength][];
269: for (int j = 2; j < extraLength; j++) {
270: this .extra[j] = new long[length];
271: }
272: } else {
273: int oldLength; // might need to grow the arrays
274: if (vectorIndex >= (oldLength = this .extra[2].length)) {
275: for (int j = 2; j < extraLength; j++) {
276: System
277: .arraycopy(
278: this .extra[j],
279: 0,
280: (this .extra[j] = new long[vectorIndex + 1]),
281: 0, oldLength);
282: }
283: }
284: }
285: this .extra[5][vectorIndex] |= (1L << (position % BitCacheSize));
286: if (coverageTestFlag && coverageTestId == 300) {
287: this .extra[5][vectorIndex] = ~0;
288: }
289: }
290: }
291: }
292:
293: /**
294: * Mitigate the definite and protected info of flowInfo, depending on what
295: * this null info registry knows about potential assignments and messages
296: * sends involving locals. May return flowInfo unchanged, or a modified,
297: * fresh copy of flowInfo.
298: * @param flowInfo - FlowInfo: the flow information that this null info
299: * registry may mitigate
300: * @return a copy of flowInfo carrying mitigated information, or else
301: * flowInfo unchanged
302: */
303: public UnconditionalFlowInfo mitigateNullInfoOf(FlowInfo flowInfo) {
304: if ((this .tagBits & NULL_FLAG_MASK) == 0) {
305: return flowInfo.unconditionalInits();
306: }
307: long m, m1, nm1, m2, nm2, m3, a2, a3, a4, s1, s2, ns2, s3, ns3, s4, ns4;
308: boolean newCopy = false;
309: UnconditionalFlowInfo source = flowInfo.unconditionalInits();
310: // clear incompatible protections
311: m1 = (s1 = source.nullBit1) & (s3 = source.nullBit3)
312: & (s4 = source.nullBit4)
313: // prot. non null
314: & ((a2 = this .nullBit2) | (a4 = this .nullBit4));
315: // null or unknown
316: m2 = s1 & (s2 = this .nullBit2) & (s3 ^ s4)
317: // prot. null
318: & ((a3 = this .nullBit3) | a4);
319: // non null or unknown
320: // clear incompatible assignments
321: // PREMATURE check effect of protected non null (no NPE on call)
322: // TODO (maxime) code extensive implementation tests
323: m3 = s1
324: & (s2 & (ns3 = ~s3) & (ns4 = ~s4) & (a3 | a4)
325: | (ns2 = ~s2) & s3 & ns4 & (a2 | a4) | ns2
326: & ns3 & s4 & (a2 | a3));
327: if ((m = (m1 | m2 | m3)) != 0) {
328: newCopy = true;
329: source = source.unconditionalCopy();
330: source.nullBit1 &= ~m;
331: source.nullBit2 &= (nm1 = ~m1) & ((nm2 = ~m2) | a4);
332: source.nullBit3 &= (nm1 | a2) & nm2;
333: source.nullBit4 &= nm1 & nm2;
334: }
335: if (this .extra != null && source.extra != null) {
336: int length = this .extra[2].length, sourceLength = source.extra[0].length;
337: if (sourceLength < length) {
338: length = sourceLength;
339: }
340: for (int i = 0; i < length; i++) {
341: m1 = (s1 = source.extra[1 + 1][i])
342: & (s3 = source.extra[3 + 1][i])
343: & (s4 = source.extra[4 + 1][i])
344: & ((a2 = this .extra[2 + 1][i]) | (a4 = this .extra[4 + 1][i]));
345: m2 = s1 & (s2 = this .extra[2 + 1][i]) & (s3 ^ s4)
346: & ((a3 = this .extra[3 + 1][i]) | a4);
347: m3 = s1
348: & (s2 & (ns3 = ~s3) & (ns4 = ~s4) & (a3 | a4)
349: | (ns2 = ~s2) & s3 & ns4 & (a2 | a4) | ns2
350: & ns3 & s4 & (a2 | a3));
351: if ((m = (m1 | m2 | m3)) != 0) {
352: if (!newCopy) {
353: newCopy = true;
354: source = source.unconditionalCopy();
355: }
356: source.extra[1 + 1][i] &= ~m;
357: source.extra[2 + 1][i] &= (nm1 = ~m1)
358: & ((nm2 = ~m2) | a4);
359: source.extra[3 + 1][i] &= (nm1 | a2) & nm2;
360: source.extra[4 + 1][i] &= nm1 & nm2;
361: }
362: }
363: }
364: return source;
365: }
366:
367: public String toString() {
368: if (this .extra == null) {
369: return "NullInfoRegistry<" + this .nullBit1 //$NON-NLS-1$
370: + this .nullBit2 + this .nullBit3 + this .nullBit4
371: + ">"; //$NON-NLS-1$
372: } else {
373: String nullS = "NullInfoRegistry<[" + this .nullBit1 //$NON-NLS-1$
374: + this .nullBit2 + this .nullBit3 + this .nullBit4;
375: int i, ceil;
376: for (i = 0, ceil = this .extra[0].length > 3 ? 3
377: : this .extra[0].length; i < ceil; i++) {
378: nullS += "," + this .extra[2][i] //$NON-NLS-1$
379: + this .extra[3][i] + this .extra[4][i]
380: + this .extra[5][i];
381: }
382: if (ceil < this .extra[0].length) {
383: nullS += ",..."; //$NON-NLS-1$
384: }
385: return nullS + "]>"; //$NON-NLS-1$
386: }
387: }
388: }
|