/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.flow; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.Reference; import org.eclipse.jdt.internal.compiler.codegen.Label; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; /** * Reflects the context of code analysis, keeping track of enclosing * try statements, exception handlers, etc... */ public class LoopingFlowContext extends SwitchFlowContext { public Label continueLabel; public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END; Reference finalAssignments[]; VariableBinding finalVariables[]; int assignCount = 0; Expression[] nullReferences; int[] nullStatus; int nullCount; Scope associatedScope; public LoopingFlowContext( FlowContext parent, ASTNode associatedNode, Label breakLabel, Label continueLabel, Scope associatedScope) { super(parent, associatedNode, breakLabel); this.continueLabel = continueLabel; this.associatedScope = associatedScope; } public void complainOnDeferredChecks(BlockScope scope, FlowInfo flowInfo) { // complain on final assignments in loops for (int i = 0; i < assignCount; i++) { VariableBinding variable = finalVariables[i]; if (variable == null) continue; boolean complained = false; // remember if have complained on this final assignment if (variable instanceof FieldBinding) { if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) { complained = true; scope.problemReporter().duplicateInitializationOfBlankFinalField( (FieldBinding) variable, finalAssignments[i]); } } else { if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) { complained = true; scope.problemReporter().duplicateInitializationOfFinalLocal( (LocalVariableBinding) variable, finalAssignments[i]); } } // any reference reported at this level is removed from the parent context where it // could also be reported again if (complained) { FlowContext context = parent; while (context != null) { context.removeFinalAssignmentIfAny(finalAssignments[i]); context = context.parent; } } } // check inconsistent null checks for (int i = 0; i < nullCount; i++) { Expression expression = nullReferences[i]; if (expression == null) continue; // final local variable LocalVariableBinding local = expression.localVariableBinding(); switch (nullStatus[i]) { case FlowInfo.NULL : if (flowInfo.isDefinitelyNull(local)) { nullReferences[i] = null; this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); } break; case FlowInfo.NON_NULL : if (flowInfo.isDefinitelyNonNull(local)) { nullReferences[i] = null; this.parent.recordUsingNullReference(scope, local, expression, nullStatus[i], flowInfo); } break; } } } public Label continueLabel() { return continueLabel; } public String individualToString() { StringBuffer buffer = new StringBuffer("Looping flow context"); //$NON-NLS-1$ buffer.append("[initsOnBreak - ").append(initsOnBreak.toString()).append(']'); //$NON-NLS-1$ buffer.append("[initsOnContinue - ").append(initsOnContinue.toString()).append(']'); //$NON-NLS-1$ buffer.append("[finalAssignments count - ").append(assignCount).append(']'); //$NON-NLS-1$ buffer.append("[nullReferences count - ").append(nullCount).append(']'); //$NON-NLS-1$ return buffer.toString(); } public boolean isContinuable() { return true; } public boolean isContinuedTo() { return initsOnContinue != FlowInfo.DEAD_END; } public void recordContinueFrom(FlowInfo flowInfo) { if (!flowInfo.isReachable()) return; if (initsOnContinue == FlowInfo.DEAD_END) { initsOnContinue = flowInfo.copy().unconditionalInits(); } else { initsOnContinue = initsOnContinue.mergedWith(flowInfo.copy().unconditionalInits()); } } protected boolean recordFinalAssignment( VariableBinding binding, Reference finalAssignment) { // do not consider variables which are defined inside this loop if (binding instanceof LocalVariableBinding) { Scope scope = ((LocalVariableBinding) binding).declaringScope; while ((scope = scope.parent) != null) { if (scope == associatedScope) return false; } } if (assignCount == 0) { finalAssignments = new Reference[5]; finalVariables = new VariableBinding[5]; } else { if (assignCount == finalAssignments.length) System.arraycopy( finalAssignments, 0, (finalAssignments = new Reference[assignCount * 2]), 0, assignCount); System.arraycopy( finalVariables, 0, (finalVariables = new VariableBinding[assignCount * 2]), 0, assignCount); } finalAssignments[assignCount] = finalAssignment; finalVariables[assignCount++] = binding; return true; } protected boolean recordNullReference(Expression expression, int status) { if (nullCount == 0) { nullReferences = new Expression[5]; nullStatus = new int[5]; } else { if (nullCount == nullReferences.length) { System.arraycopy(nullReferences, 0, nullReferences = new Expression[nullCount * 2], 0, nullCount); System.arraycopy(nullStatus, 0, nullStatus = new int[nullCount * 2], 0, nullCount); } } nullReferences[nullCount] = expression; nullStatus[nullCount++] = status; return true; } void removeFinalAssignmentIfAny(Reference reference) { for (int i = 0; i < assignCount; i++) { if (finalAssignments[i] == reference) { finalAssignments[i] = null; finalVariables[i] = null; return; } } } }