void FindMultipleEnumeration(VariableReferenceNode startNode) { var vrg = GetAssignmentUsageGraph(startNode); visitedNodes = new HashSet <VariableReferenceNode> (); collectedNodes = new HashSet <VariableReferenceNode> (); // degree of a node is the number of references that can be reached by the node nodeDegree = new Dictionary <VariableReferenceNode, int> (); foreach (var node in vrg) { if (node.References.Count == 0 || !visitedNodes.Add(node)) { continue; } ProcessNode(node); if (nodeDegree [node] > 1) { collectedNodes.Add(node); } } foreach (var node in collectedNodes) { AddIssues(node.References); } }
/// <summary> /// convert a variable reference graph starting from the specified node to an assignment usage graph, /// in which nodes are connect if and only if they contains references using the same assigned value /// </summary> /// <param name="startNode">starting node of the variable reference graph</param> /// <returns> /// list of VariableReferenceNode, each of which is a starting node of a sub-graph in which references all /// use the same assigned value /// </returns> static IEnumerable <VariableReferenceNode> GetAssignmentUsageGraph(VariableReferenceNode startNode) { var graph = new List <VariableReferenceNode> (); var visited = new HashSet <VariableReferenceNode> (); var stack = new Stack <VariableReferenceNode> (); stack.Push(startNode); while (stack.Count > 0) { var node = stack.Pop(); if (!visited.Add(node)) { continue; } var nodes = SplitNode(node); graph.AddRange(nodes); foreach (var addedNode in nodes) { visited.Add(addedNode); } foreach (var nextNode in nodes.Last().NextNodes) { stack.Push(nextNode); } } return(graph); }
/// <summary> /// split references in the specified node into sub nodes according to the value they uses /// </summary> /// <param name="node">node to split</param> /// <returns>list of sub nodes</returns> static IList <VariableReferenceNode> SplitNode(VariableReferenceNode node) { var subNodes = new List <VariableReferenceNode> (); // find indices of all assignments in node and use them to split references var assignmentIndices = new List <int> { -1 }; for (int i = 0; i < node.References.Count; i++) { if (IsAssignment(node.References [i])) { assignmentIndices.Add(i); } } assignmentIndices.Add(node.References.Count); for (int i = 0; i < assignmentIndices.Count - 1; i++) { var index1 = assignmentIndices [i]; var index2 = assignmentIndices [i + 1]; if (index1 + 1 >= index2) { continue; } var subNode = new VariableReferenceNode(); for (int refIndex = index1 + 1; refIndex < index2; refIndex++) { subNode.References.Add(node.References [refIndex]); } subNodes.Add(subNode); } if (subNodes.Count == 0) { subNodes.Add(new VariableReferenceNode()); } var firstNode = subNodes [0]; foreach (var prevNode in node.PreviousNodes) { prevNode.NextNodes.Remove(node); // connect two nodes if the first ref is not an assignment if (firstNode.References.FirstOrDefault() == node.References.FirstOrDefault()) { prevNode.NextNodes.Add(firstNode); } } var lastNode = subNodes [subNodes.Count - 1]; foreach (var nextNode in node.NextNodes) { nextNode.PreviousNodes.Remove(node); lastNode.AddNextNode(nextNode); } return(subNodes); }
void ProcessNode(VariableReferenceNode node, bool addIssue, IDictionary <VariableReferenceNode, NodeState> nodeStates) { if (nodeStates [node] == NodeState.None) { nodeStates [node] = NodeState.Processing; } bool?reachable = false; foreach (var nextNode in node.NextNodes) { if (nodeStates [nextNode] == NodeState.None) { ProcessNode(nextNode, false, nodeStates); } if (nodeStates [nextNode] == NodeState.UsageReachable) { reachable = true; break; } // downstream nodes are not fully processed (e.g. due to loop), there is no enough info to // determine the node state if (nodeStates [nextNode] != NodeState.UsageUnreachable) { reachable = null; } } // add issue if it is not possible to reach any usage via NextNodes if (addIssue && reachable == false) { AddIssue(node.References [node.References.Count - 1]); } if (nodeStates [node] != NodeState.Processing) { return; } switch (reachable) { case null: nodeStates [node] = NodeState.None; break; case true: nodeStates [node] = NodeState.UsageReachable; break; case false: nodeStates [node] = NodeState.UsageUnreachable; break; } }
private StyleASTNode ParseVariableReference() { AdvanceIfTokenType(StyleTokenType.Dollar); VariableReferenceNode refNode = new VariableReferenceNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)); while (tokenStream.HasMoreTokens && AdvanceIfTokenType(StyleTokenType.Dot)) { refNode.AddChildNode(StyleASTNodeFactory.DotAccessNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)).WithLocation(tokenStream.Previous)); } return(refNode); }
void ProcessNode(VariableReferenceNode node) { var degree = nodeDegree [node] = 0; foreach (var nextNode in node.NextNodes) { collectedNodes.Add(nextNode); if (visitedNodes.Add(nextNode)) { ProcessNode(nextNode); } degree = Math.Max(degree, nodeDegree [nextNode]); } nodeDegree [node] = degree + node.References.Count; }
void ProcessNode(VariableReferenceNode node) { var degree = nodeDegree [node] = 0; foreach (var nextNode in node.NextNodes) { collectedNodes.Add (nextNode); if (visitedNodes.Add (nextNode)) ProcessNode (nextNode); degree = Math.Max (degree, nodeDegree [nextNode]); } nodeDegree [node] = degree + node.References.Count; }
void FindMultipleEnumeration(VariableReferenceNode startNode) { var vrg = GetAssignmentUsageGraph (startNode); visitedNodes = new HashSet<VariableReferenceNode> (); collectedNodes = new HashSet<VariableReferenceNode> (); // degree of a node is the number of references that can be reached by the node nodeDegree = new Dictionary<VariableReferenceNode, int> (); foreach (var node in vrg) { if (node.References.Count == 0 || !visitedNodes.Add (node)) continue; ProcessNode (node); if (nodeDegree [node] > 1) collectedNodes.Add (node); } foreach (var node in collectedNodes) AddIssues (node.References); }
/// <summary> /// split references in the specified node into sub nodes according to the value they uses /// </summary> /// <param name="node">node to split</param> /// <returns>list of sub nodes</returns> static IList<VariableReferenceNode> SplitNode(VariableReferenceNode node) { var subNodes = new List<VariableReferenceNode> (); // find indices of all assignments in node and use them to split references var assignmentIndices = new List<int> { -1 }; for (int i = 0; i < node.References.Count; i++) { if (IsAssignment (node.References [i])) assignmentIndices.Add (i); } assignmentIndices.Add (node.References.Count); for (int i = 0; i < assignmentIndices.Count - 1; i++) { var index1 = assignmentIndices [i]; var index2 = assignmentIndices [i + 1]; if (index1 + 1 >= index2) continue; var subNode = new VariableReferenceNode (); for (int refIndex = index1 + 1; refIndex < index2; refIndex++) subNode.References.Add (node.References [refIndex]); subNodes.Add (subNode); } if (subNodes.Count == 0) subNodes.Add (new VariableReferenceNode ()); var firstNode = subNodes [0]; foreach (var prevNode in node.PreviousNodes) { prevNode.NextNodes.Remove (node); // connect two nodes if the first ref is not an assignment if (firstNode.References.FirstOrDefault () == node.References.FirstOrDefault ()) prevNode.NextNodes.Add (firstNode); } var lastNode = subNodes [subNodes.Count - 1]; foreach (var nextNode in node.NextNodes) { nextNode.PreviousNodes.Remove (node); lastNode.AddNextNode (nextNode); } return subNodes; }
/// <summary> /// convert a variable reference graph starting from the specified node to an assignment usage graph, /// in which nodes are connect if and only if they contains references using the same assigned value /// </summary> /// <param name="startNode">starting node of the variable reference graph</param> /// <returns> /// list of VariableReferenceNode, each of which is a starting node of a sub-graph in which references all /// use the same assigned value /// </returns> static IEnumerable<VariableReferenceNode> GetAssignmentUsageGraph(VariableReferenceNode startNode) { var graph = new List<VariableReferenceNode> (); var visited = new HashSet<VariableReferenceNode> (); var stack = new Stack<VariableReferenceNode> (); stack.Push (startNode); while (stack.Count > 0) { var node = stack.Pop (); if (!visited.Add (node)) continue; var nodes = SplitNode (node); graph.AddRange (nodes); foreach (var addedNode in nodes) visited.Add (addedNode); foreach (var nextNode in nodes.Last ().NextNodes) stack.Push (nextNode); } return graph; }
void ProcessNodes(VariableReferenceNode startNode) { // node state of a node indicates whether it is possible for an upstream node to reach any usage via // the node var nodeStates = new Dictionary<VariableReferenceNode, NodeState> (); var assignments = new List<VariableReferenceNode> (); // dfs to preprocess all nodes and find nodes which end with assignment var stack = new Stack<VariableReferenceNode> (); stack.Push (startNode); while (stack.Count > 0) { var node = stack.Pop (); if (node.References.Count > 0) { nodeStates [node] = IsAssignment (node.References [0]) ? NodeState.UsageUnreachable : NodeState.UsageReachable; } else { nodeStates [node] = NodeState.None; } // find indices of all assignments in node.References var assignmentIndices = new List<int> (); for (int i = 0; i < node.References.Count; i++) { if (IsAssignment (node.References [i])) assignmentIndices.Add (i); } // for two consecutive assignments, the first one is redundant for (int i = 0; i < assignmentIndices.Count - 1; i++) { var index1 = assignmentIndices [i]; var index2 = assignmentIndices [i + 1]; if (index1 + 1 == index2) AddIssue (node.References [index1]); } // if the node ends with an assignment, add it to assignments so as to check if it is redundant // later if (assignmentIndices.Count > 0 && assignmentIndices [assignmentIndices.Count - 1] == node.References.Count - 1) assignments.Add (node); foreach (var nextNode in node.NextNodes) { if (!nodeStates.ContainsKey (nextNode)) stack.Push (nextNode); } } foreach (var node in assignments) { // we do not analyze an assignment inside a try block as it can jump to any catch block or finally block if (IsInsideTryBlock(node.References[0])) continue; ProcessNode(node, true, nodeStates); } }
void ProcessNode(VariableReferenceNode node, bool addIssue, IDictionary<VariableReferenceNode, NodeState> nodeStates) { if (nodeStates [node] == NodeState.None) nodeStates [node] = NodeState.Processing; bool? reachable = false; foreach (var nextNode in node.NextNodes) { if (nodeStates [nextNode] == NodeState.None) ProcessNode (nextNode, false, nodeStates); if (nodeStates [nextNode] == NodeState.UsageReachable) { reachable = true; break; } // downstream nodes are not fully processed (e.g. due to loop), there is no enough info to // determine the node state if (nodeStates [nextNode] != NodeState.UsageUnreachable) reachable = null; } // add issue if it is not possible to reach any usage via NextNodes if (addIssue && reachable == false) AddIssue (node.References [node.References.Count - 1]); if (nodeStates [node] != NodeState.Processing) return; switch (reachable) { case null: nodeStates [node] = NodeState.None; break; case true: nodeStates [node] = NodeState.UsageReachable; break; case false: nodeStates [node] = NodeState.UsageUnreachable; break; } }
void ProcessNodes(VariableReferenceNode startNode) { // node state of a node indicates whether it is possible for an upstream node to reach any usage via // the node var nodeStates = new Dictionary <VariableReferenceNode, NodeState>(); var assignments = new List <VariableReferenceNode>(); // dfs to preprocess all nodes and find nodes which end with assignment var stack = new Stack <VariableReferenceNode>(); stack.Push(startNode); while (stack.Count > 0) { var node = stack.Pop(); if (node == null) { continue; } if (node.References.Count > 0) { nodeStates [node] = IsAssignment(node.References [0]) ? NodeState.UsageUnreachable : NodeState.UsageReachable; } else { nodeStates [node] = NodeState.None; } // find indices of all assignments in node.References var assignmentIndices = new List <int>(); for (int i = 0; i < node.References.Count; i++) { if (IsAssignment(node.References [i])) { assignmentIndices.Add(i); } } // for two consecutive assignments, the first one is redundant for (int i = 0; i < assignmentIndices.Count - 1; i++) { var index1 = assignmentIndices [i]; var index2 = assignmentIndices [i + 1]; if (index1 + 1 == index2) { AddIssue(node.References [index1]); } } // if the node ends with an assignment, add it to assignments so as to check if it is redundant // later if (assignmentIndices.Count > 0 && assignmentIndices [assignmentIndices.Count - 1] == node.References.Count - 1) { assignments.Add(node); } foreach (var nextNode in node.NextNodes) { if (!nodeStates.ContainsKey(nextNode)) { stack.Push(nextNode); } } } foreach (var node in assignments) { // we do not analyze an assignment inside a try block as it can jump to any catch block or finally block if (IsInsideTryOrCatchBlock(node.References [0])) { continue; } ProcessNode(node, true, nodeStates); } }
void ProcessNode (VariableReferenceNode node, bool addIssue, IDictionary<VariableReferenceNode, NodeState> nodeStates) { if (nodeStates [node] == NodeState.None) nodeStates [node] = NodeState.Processing; bool? reachable = false; foreach (var nextNode in node.NextNodes) { if (nodeStates [nextNode] == NodeState.None) ProcessNode (nextNode, false, nodeStates); if (nodeStates [nextNode] == NodeState.UsageReachable) { reachable = true; break; } if (nodeStates [nextNode] == NodeState.Processing) reachable = null; } // not possible to reach any usage via NextNodes if (addIssue && reachable == false) AddIssue (node.References [node.References.Count - 1]); if (nodeStates [node] != NodeState.Processing) return; switch (reachable) { case null: nodeStates [node] = NodeState.None; break; case true: nodeStates [node] = NodeState.UsageReachable; break; case false: nodeStates [node] = NodeState.UsageUnreachable; break; } }
void ProcessNodes (VariableReferenceNode startNode) { var nodeStates = new Dictionary<VariableReferenceNode, NodeState> (); var assignments = new List<VariableReferenceNode> (); // dfs to preprocess all nodes and find nodes which end with assignment var stack = new Stack<VariableReferenceNode> (); stack.Push (startNode); while (stack.Count > 0) { var node = stack.Pop (); if (node.References.Count > 0) { nodeStates [node] = IsAssignment (node.References [0]) ? NodeState.UsageUnreachable : NodeState.UsageReachable; } else { nodeStates [node] = NodeState.None; } var assignmentIndexes = new List<int> (); for (int i = 0; i < node.References.Count; i++) { if (IsAssignment (node.References [i])) assignmentIndexes.Add (i); } for (int i = 0; i < assignmentIndexes.Count - 1; i++) { var index1 = assignmentIndexes [i]; var index2 = assignmentIndexes [i + 1]; if (index1 + 1 == index2) AddIssue (node.References [index1]); } if (assignmentIndexes.Count > 0 && assignmentIndexes [assignmentIndexes.Count - 1] == node.References.Count - 1) assignments.Add (node); foreach (var nextNode in node.NextNodes) { if (!nodeStates.ContainsKey (nextNode)) stack.Push (nextNode); } } foreach (var node in assignments) ProcessNode (node, true, nodeStates); }