| /******************************************************************************* |
| * 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; |
| } |
| } |
| } |
| } |