001: /*******************************************************************************
002: * Copyright (c) 2000, 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.ast.ASTNode;
013: import org.eclipse.jdt.internal.compiler.ast.Expression;
014: import org.eclipse.jdt.internal.compiler.ast.Reference;
015: import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
016: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
017: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
018: import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
019: import org.eclipse.jdt.internal.compiler.lookup.Scope;
020: import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
021:
022: /**
023: * Reflects the context of code analysis, keeping track of enclosing
024: * try statements, exception handlers, etc...
025: */
026: public class LoopingFlowContext extends SwitchFlowContext {
027:
028: public BranchLabel continueLabel;
029: public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END;
030: private UnconditionalFlowInfo upstreamNullFlowInfo;
031: private LoopingFlowContext innerFlowContexts[] = null;
032: private UnconditionalFlowInfo innerFlowInfos[] = null;
033: private int innerFlowContextsCount = 0;
034: private LabelFlowContext breakTargetContexts[] = null;
035: private int breakTargetsCount = 0;
036:
037: Reference finalAssignments[];
038: VariableBinding finalVariables[];
039: int assignCount = 0;
040:
041: LocalVariableBinding[] nullLocals;
042: Expression[] nullReferences;
043: int[] nullCheckTypes;
044: int nullCount;
045:
046: Scope associatedScope;
047:
048: public LoopingFlowContext(FlowContext parent,
049: FlowInfo upstreamNullFlowInfo, ASTNode associatedNode,
050: BranchLabel breakLabel, BranchLabel continueLabel,
051: Scope associatedScope) {
052: super (parent, associatedNode, breakLabel);
053: preemptNullDiagnostic = true;
054: // children will defer to this, which may defer to its own parent
055: this .continueLabel = continueLabel;
056: this .associatedScope = associatedScope;
057: this .upstreamNullFlowInfo = upstreamNullFlowInfo
058: .unconditionalCopy();
059: }
060:
061: /**
062: * Perform deferred checks relative to final variables duplicate initialization
063: * of lack of initialization.
064: * @param scope the scope to which this context is associated
065: * @param flowInfo the flow info against which checks must be performed
066: */
067: public void complainOnDeferredFinalChecks(BlockScope scope,
068: FlowInfo flowInfo) {
069: // complain on final assignments in loops
070: for (int i = 0; i < assignCount; i++) {
071: VariableBinding variable = finalVariables[i];
072: if (variable == null)
073: continue;
074: boolean complained = false; // remember if have complained on this final assignment
075: if (variable instanceof FieldBinding) {
076: if (flowInfo
077: .isPotentiallyAssigned((FieldBinding) variable)) {
078: complained = true;
079: scope.problemReporter()
080: .duplicateInitializationOfBlankFinalField(
081: (FieldBinding) variable,
082: finalAssignments[i]);
083: }
084: } else {
085: if (flowInfo
086: .isPotentiallyAssigned((LocalVariableBinding) variable)) {
087: complained = true;
088: scope.problemReporter()
089: .duplicateInitializationOfFinalLocal(
090: (LocalVariableBinding) variable,
091: finalAssignments[i]);
092: }
093: }
094: // any reference reported at this level is removed from the parent context where it
095: // could also be reported again
096: if (complained) {
097: FlowContext context = parent;
098: while (context != null) {
099: context
100: .removeFinalAssignmentIfAny(finalAssignments[i]);
101: context = context.parent;
102: }
103: }
104: }
105: }
106:
107: /**
108: * Perform deferred checks relative to the null status of local variables.
109: * @param scope the scope to which this context is associated
110: * @param callerFlowInfo the flow info against which checks must be performed
111: */
112: public void complainOnDeferredNullChecks(BlockScope scope,
113: FlowInfo callerFlowInfo) {
114: for (int i = 0; i < this .innerFlowContextsCount; i++) {
115: this .upstreamNullFlowInfo.addPotentialNullInfoFrom(
116: this .innerFlowContexts[i].upstreamNullFlowInfo)
117: .addPotentialNullInfoFrom(this .innerFlowInfos[i]);
118: }
119: this .innerFlowContextsCount = 0;
120: UnconditionalFlowInfo flowInfo = this .upstreamNullFlowInfo
121: .addPotentialNullInfoFrom(callerFlowInfo
122: .unconditionalInitsWithoutSideEffect());
123: if (this .deferNullDiagnostic) {
124: // check only immutable null checks on innermost looping context
125: for (int i = 0; i < this .nullCount; i++) {
126: LocalVariableBinding local = this .nullLocals[i];
127: Expression expression = this .nullReferences[i];
128: // final local variable
129: switch (this .nullCheckTypes[i]) {
130: case CAN_ONLY_NON_NULL | IN_COMPARISON_NULL:
131: case CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL:
132: if (flowInfo.isDefinitelyNonNull(local)) {
133: this .nullReferences[i] = null;
134: if (this .nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) {
135: scope
136: .problemReporter()
137: .localVariableRedundantCheckOnNonNull(
138: local, expression);
139: } else {
140: scope
141: .problemReporter()
142: .localVariableNonNullComparedToNull(
143: local, expression);
144: }
145: continue;
146: }
147: break;
148: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
149: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
150: if (flowInfo.isDefinitelyNonNull(local)) {
151: this .nullReferences[i] = null;
152: if (this .nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
153: scope
154: .problemReporter()
155: .localVariableRedundantCheckOnNonNull(
156: local, expression);
157: } else {
158: scope
159: .problemReporter()
160: .localVariableNonNullComparedToNull(
161: local, expression);
162: }
163: continue;
164: }
165: if (flowInfo.isDefinitelyNull(local)) {
166: this .nullReferences[i] = null;
167: if (this .nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
168: scope.problemReporter()
169: .localVariableRedundantCheckOnNull(
170: local, expression);
171: } else {
172: scope
173: .problemReporter()
174: .localVariableNullComparedToNonNull(
175: local, expression);
176: }
177: continue;
178: }
179: break;
180: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
181: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
182: case CAN_ONLY_NULL | IN_ASSIGNMENT:
183: case CAN_ONLY_NULL | IN_INSTANCEOF:
184: if (flowInfo.isDefinitelyNull(local)) {
185: this .nullReferences[i] = null;
186: switch (this .nullCheckTypes[i] & CONTEXT_MASK) {
187: case FlowContext.IN_COMPARISON_NULL:
188: scope.problemReporter()
189: .localVariableRedundantCheckOnNull(
190: local, expression);
191: continue;
192: case FlowContext.IN_COMPARISON_NON_NULL:
193: scope
194: .problemReporter()
195: .localVariableNullComparedToNonNull(
196: local, expression);
197: continue;
198: case FlowContext.IN_ASSIGNMENT:
199: scope
200: .problemReporter()
201: .localVariableRedundantNullAssignment(
202: local, expression);
203: continue;
204: case FlowContext.IN_INSTANCEOF:
205: scope.problemReporter()
206: .localVariableNullInstanceof(local,
207: expression);
208: continue;
209: }
210: }
211: break;
212: case MAY_NULL:
213: if (flowInfo.isDefinitelyNull(local)) {
214: this .nullReferences[i] = null;
215: scope.problemReporter()
216: .localVariableNullReference(local,
217: expression);
218: continue;
219: }
220: break;
221: default:
222: // never happens
223: }
224: this .parent.recordUsingNullReference(scope, local,
225: expression, this .nullCheckTypes[i], flowInfo);
226: }
227: } else {
228: // check inconsistent null checks on outermost looping context
229: for (int i = 0; i < this .nullCount; i++) {
230: Expression expression = this .nullReferences[i];
231: // final local variable
232: LocalVariableBinding local = this .nullLocals[i];
233: switch (this .nullCheckTypes[i]) {
234: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
235: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
236: if (flowInfo.isDefinitelyNonNull(local)) {
237: this .nullReferences[i] = null;
238: if (this .nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
239: scope
240: .problemReporter()
241: .localVariableRedundantCheckOnNonNull(
242: local, expression);
243: } else {
244: scope
245: .problemReporter()
246: .localVariableNonNullComparedToNull(
247: local, expression);
248: }
249: continue;
250: }
251: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
252: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
253: case CAN_ONLY_NULL | IN_ASSIGNMENT:
254: case CAN_ONLY_NULL | IN_INSTANCEOF:
255: if (flowInfo.isDefinitelyNull(local)) {
256: this .nullReferences[i] = null;
257: switch (this .nullCheckTypes[i] & CONTEXT_MASK) {
258: case FlowContext.IN_COMPARISON_NULL:
259: scope.problemReporter()
260: .localVariableRedundantCheckOnNull(
261: local, expression);
262: continue;
263: case FlowContext.IN_COMPARISON_NON_NULL:
264: scope
265: .problemReporter()
266: .localVariableNullComparedToNonNull(
267: local, expression);
268: continue;
269: case FlowContext.IN_ASSIGNMENT:
270: scope
271: .problemReporter()
272: .localVariableRedundantNullAssignment(
273: local, expression);
274: continue;
275: case FlowContext.IN_INSTANCEOF:
276: scope.problemReporter()
277: .localVariableNullInstanceof(local,
278: expression);
279: continue;
280: }
281: }
282: break;
283: case MAY_NULL:
284: if (flowInfo.isDefinitelyNull(local)) {
285: this .nullReferences[i] = null;
286: scope.problemReporter()
287: .localVariableNullReference(local,
288: expression);
289: continue;
290: }
291: if (flowInfo.isPotentiallyNull(local)) {
292: this .nullReferences[i] = null;
293: scope.problemReporter()
294: .localVariablePotentialNullReference(
295: local, expression);
296: continue;
297: }
298: break;
299: default:
300: // never happens
301: }
302: }
303: }
304: // propagate breaks
305: this .initsOnBreak.addPotentialNullInfoFrom(flowInfo);
306: for (int i = 0; i < this .breakTargetsCount; i++) {
307: this .breakTargetContexts[i].initsOnBreak
308: .addPotentialNullInfoFrom(flowInfo);
309: }
310: }
311:
312: public BranchLabel continueLabel() {
313: return continueLabel;
314: }
315:
316: public String individualToString() {
317: StringBuffer buffer = new StringBuffer("Looping flow context"); //$NON-NLS-1$
318: buffer
319: .append("[initsOnBreak - ").append(initsOnBreak.toString()).append(']'); //$NON-NLS-1$
320: buffer
321: .append("[initsOnContinue - ").append(initsOnContinue.toString()).append(']'); //$NON-NLS-1$
322: buffer
323: .append("[finalAssignments count - ").append(assignCount).append(']'); //$NON-NLS-1$
324: buffer
325: .append("[nullReferences count - ").append(nullCount).append(']'); //$NON-NLS-1$
326: return buffer.toString();
327: }
328:
329: public boolean isContinuable() {
330: return true;
331: }
332:
333: public boolean isContinuedTo() {
334: return initsOnContinue != FlowInfo.DEAD_END;
335: }
336:
337: public void recordBreakTo(FlowContext targetContext) {
338: if (targetContext instanceof LabelFlowContext) {
339: int current;
340: if ((current = this .breakTargetsCount++) == 0) {
341: this .breakTargetContexts = new LabelFlowContext[2];
342: } else if (current == this .breakTargetContexts.length) {
343: System
344: .arraycopy(
345: this .breakTargetContexts,
346: 0,
347: this .breakTargetContexts = new LabelFlowContext[current + 2],
348: 0, current);
349: }
350: this .breakTargetContexts[current] = (LabelFlowContext) targetContext;
351: }
352: }
353:
354: public void recordContinueFrom(FlowContext innerFlowContext,
355: FlowInfo flowInfo) {
356: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
357: if ((initsOnContinue.tagBits & FlowInfo.UNREACHABLE) == 0) {
358: initsOnContinue = initsOnContinue.mergedWith(flowInfo
359: .unconditionalInitsWithoutSideEffect());
360: } else {
361: initsOnContinue = flowInfo.unconditionalCopy();
362: }
363: FlowContext inner = innerFlowContext;
364: while (inner != this
365: && !(inner instanceof LoopingFlowContext)) {
366: inner = inner.parent;
367: }
368: if (inner == this ) {
369: this .upstreamNullFlowInfo
370: .addPotentialNullInfoFrom(flowInfo
371: .unconditionalInitsWithoutSideEffect());
372: } else {
373: int length = 0;
374: if (this .innerFlowContexts == null) {
375: this .innerFlowContexts = new LoopingFlowContext[5];
376: this .innerFlowInfos = new UnconditionalFlowInfo[5];
377: } else if (this .innerFlowContextsCount == (length = this .innerFlowContexts.length) - 1) {
378: System
379: .arraycopy(
380: this .innerFlowContexts,
381: 0,
382: (this .innerFlowContexts = new LoopingFlowContext[length + 5]),
383: 0, length);
384: System
385: .arraycopy(
386: this .innerFlowInfos,
387: 0,
388: (this .innerFlowInfos = new UnconditionalFlowInfo[length + 5]),
389: 0, length);
390: }
391: this .innerFlowContexts[this .innerFlowContextsCount] = (LoopingFlowContext) inner;
392: this .innerFlowInfos[this .innerFlowContextsCount++] = flowInfo
393: .unconditionalInitsWithoutSideEffect();
394: }
395: }
396: }
397:
398: protected boolean recordFinalAssignment(VariableBinding binding,
399: Reference finalAssignment) {
400:
401: // do not consider variables which are defined inside this loop
402: if (binding instanceof LocalVariableBinding) {
403: Scope scope = ((LocalVariableBinding) binding).declaringScope;
404: while ((scope = scope.parent) != null) {
405: if (scope == associatedScope)
406: return false;
407: }
408: }
409: if (assignCount == 0) {
410: finalAssignments = new Reference[5];
411: finalVariables = new VariableBinding[5];
412: } else {
413: if (assignCount == finalAssignments.length)
414: System
415: .arraycopy(
416: finalAssignments,
417: 0,
418: (finalAssignments = new Reference[assignCount * 2]),
419: 0, assignCount);
420: System
421: .arraycopy(
422: finalVariables,
423: 0,
424: (finalVariables = new VariableBinding[assignCount * 2]),
425: 0, assignCount);
426: }
427: finalAssignments[assignCount] = finalAssignment;
428: finalVariables[assignCount++] = binding;
429: return true;
430: }
431:
432: protected void recordNullReference(LocalVariableBinding local,
433: Expression expression, int status) {
434: if (nullCount == 0) {
435: nullLocals = new LocalVariableBinding[5];
436: nullReferences = new Expression[5];
437: nullCheckTypes = new int[5];
438: } else if (nullCount == nullLocals.length) {
439: System
440: .arraycopy(
441: nullLocals,
442: 0,
443: nullLocals = new LocalVariableBinding[nullCount * 2],
444: 0, nullCount);
445: System.arraycopy(nullReferences, 0,
446: nullReferences = new Expression[nullCount * 2], 0,
447: nullCount);
448: System.arraycopy(nullCheckTypes, 0,
449: nullCheckTypes = new int[nullCount * 2], 0,
450: nullCount);
451: }
452: nullLocals[nullCount] = local;
453: nullReferences[nullCount] = expression;
454: nullCheckTypes[nullCount++] = status;
455: }
456:
457: public void recordUsingNullReference(Scope scope,
458: LocalVariableBinding local, Expression reference,
459: int checkType, FlowInfo flowInfo) {
460: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0
461: || flowInfo.isDefinitelyUnknown(local)) {
462: return;
463: }
464: switch (checkType) {
465: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
466: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
467: if (flowInfo.isDefinitelyNonNull(local)) {
468: if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
469: scope.problemReporter()
470: .localVariableRedundantCheckOnNonNull(
471: local, reference);
472: } else {
473: scope.problemReporter()
474: .localVariableNonNullComparedToNull(local,
475: reference);
476: }
477: } else if (flowInfo.isDefinitelyNull(local)) {
478: if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
479: scope.problemReporter()
480: .localVariableRedundantCheckOnNull(local,
481: reference);
482: } else {
483: scope.problemReporter()
484: .localVariableNullComparedToNonNull(local,
485: reference);
486: }
487: } else if (!flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
488: if (flowInfo.isPotentiallyNonNull(local)) {
489: recordNullReference(local, reference,
490: CAN_ONLY_NON_NULL | checkType
491: & CONTEXT_MASK);
492: } else {
493: recordNullReference(local, reference, checkType);
494: }
495: }
496: return;
497: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
498: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
499: case CAN_ONLY_NULL | IN_ASSIGNMENT:
500: case CAN_ONLY_NULL | IN_INSTANCEOF:
501: if (flowInfo.isPotentiallyNonNull(local)
502: || flowInfo.isPotentiallyUnknown(local)) {
503: return;
504: }
505: if (flowInfo.isDefinitelyNull(local)) {
506: switch (checkType & CONTEXT_MASK) {
507: case FlowContext.IN_COMPARISON_NULL:
508: scope.problemReporter()
509: .localVariableRedundantCheckOnNull(local,
510: reference);
511: return;
512: case FlowContext.IN_COMPARISON_NON_NULL:
513: scope.problemReporter()
514: .localVariableNullComparedToNonNull(local,
515: reference);
516: return;
517: case FlowContext.IN_ASSIGNMENT:
518: scope.problemReporter()
519: .localVariableRedundantNullAssignment(
520: local, reference);
521: return;
522: case FlowContext.IN_INSTANCEOF:
523: scope.problemReporter()
524: .localVariableNullInstanceof(local,
525: reference);
526: return;
527: }
528: }
529: recordNullReference(local, reference, checkType);
530: return;
531: case MAY_NULL:
532: if (flowInfo.isDefinitelyNonNull(local)) {
533: return;
534: }
535: if (flowInfo.isDefinitelyNull(local)) {
536: scope.problemReporter().localVariableNullReference(
537: local, reference);
538: return;
539: }
540: if (flowInfo.isPotentiallyNull(local)) {
541: scope.problemReporter()
542: .localVariablePotentialNullReference(local,
543: reference);
544: return;
545: }
546: recordNullReference(local, reference, checkType);
547: return;
548: default:
549: // never happens
550: }
551: }
552:
553: void removeFinalAssignmentIfAny(Reference reference) {
554: for (int i = 0; i < assignCount; i++) {
555: if (finalAssignments[i] == reference) {
556: finalAssignments[i] = null;
557: finalVariables[i] = null;
558: return;
559: }
560: }
561: }
562: }
|