void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus) { DefiniteAssignmentStatus oldStatus = edgeStatus[edge]; if (oldStatus == newStatus) { return; } // Ensure that status can cannot change back to CodeUnreachable after it once was reachable. // Also, don't ever use AssignedAfter... for statements. if (newStatus == DefiniteAssignmentStatus.CodeUnreachable || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { throw new InvalidOperationException(); } // Note that the status can change from DefinitelyAssigned // back to PotentiallyAssigned as unreachable input edges are // discovered to be reachable. edgeStatus[edge] = newStatus; DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To; if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) { // TODO: potential optimization: visit previously unreachable nodes with higher priority // (e.g. use Deque and enqueue previously unreachable nodes at the front, but // other nodes at the end) nodesWithModifiedInput.Enqueue(targetNode); } }
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken)) { this.analysisCancellationToken = cancellationToken; this.variableName = variable; try { // Reset the status: unassignedVariableUses.Clear(); foreach (DefiniteAssignmentNode node in allNodes) { node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable; foreach (ControlFlowEdge edge in node.Outgoing) { edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; } } ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus); // Iterate as long as the input status of some nodes is changing: while (nodesWithModifiedInput.Count > 0) { DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue(); DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; foreach (ControlFlowEdge edge in node.Incoming) { inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); } ChangeNodeStatus(node, inputStatus); } } finally { this.analysisCancellationToken = CancellationToken.None; this.variableName = null; } }
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status) { if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { return(DefiniteAssignmentStatus.PotentiallyAssigned); } else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { return(DefiniteAssignmentStatus.PotentiallyAssigned); } else { return(status); } }
public void Analyze(string variable, CancellationToken cancellationToken, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned) { this.analysisCancellationToken = cancellationToken; this.variableName = variable; try { // Reset the status: unassignedVariableUses.Clear(); foreach (DefiniteAssignmentNode node in allNodes) { node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable; foreach (ControlFlowEdge edge in node.Outgoing) { edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; } } ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus); // Iterate as long as the input status of some nodes is changing: int count = 0; const int HACK_POLL_COUNT = 0x00200000; while (nodesWithModifiedInput.Count > 0) { if (count++ >= HACK_POLL_COUNT) { this.analysisCancellationToken.ThrowIfCancellationRequested(); count = 0; } DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue(); DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; foreach (ControlFlowEdge edge in node.Incoming) { inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); } ChangeNodeStatus(node, inputStatus); } } finally { this.analysisCancellationToken = CancellationToken.None; this.variableName = null; } }
static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b) { // The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable. // The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable. // The result will be Unreachable if all incoming edges are unreachable. // Otherwise, the result will be PotentiallyAssigned. if (a == b) { return(a); } else if (a == DefiniteAssignmentStatus.CodeUnreachable) { return(b); } else if (b == DefiniteAssignmentStatus.CodeUnreachable) { return(a); } else { return(DefiniteAssignmentStatus.PotentiallyAssigned); } }
void ChangeNodeStatus(DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus) { if (node.NodeStatus == inputStatus) { return; } node.NodeStatus = inputStatus; DefiniteAssignmentStatus outputStatus; switch (node.Type) { case ControlFlowNodeType.StartNode: case ControlFlowNodeType.BetweenStatements: if (node.NextStatement is IfElseStatement) { // Handle if-else as a condition node goto case ControlFlowNodeType.LoopCondition; } if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) { // There isn't any way to un-assign variables, so we don't have to check the expression // if the status already is definitely assigned. outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; } else { outputStatus = CleanSpecialValues(node.NextStatement.AcceptVisitor(visitor, inputStatus)); } break; case ControlFlowNodeType.EndNode: outputStatus = inputStatus; if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole && (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) { TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent; // Changing the status on a finally block potentially changes the status of all edges leaving that finally block: foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) { if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains(tryFinally)) { DefiniteAssignmentStatus s = edgeStatus[edge]; if (s == DefiniteAssignmentStatus.PotentiallyAssigned) { ChangeEdgeStatus(edge, outputStatus); } } } } break; case ControlFlowNodeType.LoopCondition: ForeachStatement foreachStmt = node.NextStatement as ForeachStatement; if (foreachStmt != null) { outputStatus = CleanSpecialValues(foreachStmt.InExpression.AcceptVisitor(visitor, inputStatus)); if (foreachStmt.VariableName == this.variableName) { outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; } break; } else { Debug.Assert(node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement); Expression condition = node.NextStatement.GetChildByRole(AstNode.Roles.Condition); if (condition.IsNull) { outputStatus = inputStatus; } else { outputStatus = condition.AcceptVisitor(visitor, inputStatus); } foreach (ControlFlowEdge edge in node.Outgoing) { if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); } else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); } else { ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus)); } } return; } default: throw new InvalidOperationException(); } foreach (ControlFlowEdge edge in node.Outgoing) { ChangeEdgeStatus(edge, outputStatus); } }