| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library servicec.cycle_detection; | |
| 6 | |
| 7 import 'dart:collection' show | |
| 8 LinkedHashSet; | |
| 9 | |
| 10 import 'node.dart' show | |
| 11 MemberNode, | |
| 12 FieldNode, | |
| 13 SimpleType, | |
| 14 StructNode, | |
| 15 TypeNode, | |
| 16 UnionNode; | |
| 17 | |
| 18 import 'errors.dart' show | |
| 19 CompilationError, | |
| 20 CyclicStructError, | |
| 21 ErrorTag; | |
| 22 | |
| 23 class GraphNode { | |
| 24 GraphNodeState state = GraphNodeState.UNVISITED; | |
| 25 StructNode struct; | |
| 26 | |
| 27 GraphNode(this.struct); | |
| 28 | |
| 29 bool get isNotVisited => GraphNodeState.UNVISITED == state; | |
| 30 | |
| 31 bool operator ==(GraphNode other) { | |
| 32 return struct == other.struct; | |
| 33 } | |
| 34 int get hashCode => struct.hashCode; | |
| 35 | |
| 36 String toString() => "Node[${struct.identifier.value}, ${state.toString()}]"; | |
| 37 } | |
| 38 | |
| 39 enum GraphNodeState { | |
| 40 VISITED, | |
| 41 VISITING, | |
| 42 UNVISITED | |
| 43 } | |
| 44 | |
| 45 class StructGraph { | |
| 46 Set<GraphNode> nodes; | |
| 47 Map<GraphNode, Set<GraphNode>> neighbours; | |
| 48 | |
| 49 StructGraph() | |
| 50 : nodes = new LinkedHashSet<GraphNode>(), // Maintain node order. | |
| 51 neighbours = new Map<GraphNode, Set<GraphNode>>(); | |
| 52 | |
| 53 void add(StructNode struct) { | |
| 54 for (MemberNode member in struct.members) { | |
| 55 if (member is FieldNode) { | |
| 56 addStructField(struct, member); | |
| 57 } else { | |
| 58 UnionNode union = member; | |
| 59 union.fields.forEach((field) => addStructField(struct, field)); | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 // Helper function. | |
| 65 void addStructField(StructNode struct, FieldNode field) { | |
| 66 TypeNode type = field.type; | |
| 67 if (type != null && type.isStruct()) { | |
| 68 SimpleType simpleType = type; | |
| 69 addLink(struct, simpleType.resolved); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 void addLink(StructNode from, StructNode to) { | |
| 74 GraphNode fromNode = addNodeIfNew(from); | |
| 75 GraphNode toNode = addNodeIfNew(to); | |
| 76 neighbours[fromNode].add(toNode); | |
| 77 } | |
| 78 | |
| 79 GraphNode addNodeIfNew(StructNode node) { | |
| 80 GraphNode result = nodes.firstWhere( | |
| 81 (graphNode) => graphNode.struct == node, | |
| 82 orElse: () => null); | |
| 83 if (null == result) { | |
| 84 result = new GraphNode(node); | |
| 85 nodes.add(result); | |
| 86 neighbours[result] = new Set<GraphNode>(); | |
| 87 } | |
| 88 return result; | |
| 89 } | |
| 90 | |
| 91 List<CompilationError> findCycles() { | |
| 92 List<CompilationError> errors = <CompilationError>[]; | |
| 93 List<GraphNode> stack = new List<GraphNode>(); | |
| 94 stack.addAll(nodes.toList().reversed.toList()); | |
| 95 while (stack.isNotEmpty) { | |
| 96 GraphNode node = stack.last; | |
| 97 switch (node.state) { | |
| 98 case GraphNodeState.UNVISITED: | |
| 99 node.state = GraphNodeState.VISITING; | |
| 100 for (GraphNode neighbour in neighbours[node]) { | |
| 101 switch (neighbour.state) { | |
| 102 case GraphNodeState.UNVISITED: | |
| 103 // The `neighbour` hasn't been seen yet - add to stack. | |
| 104 stack.add(neighbour); | |
| 105 break; | |
| 106 case GraphNodeState.VISITING: | |
| 107 // The `neighbour` is in the current route from the root to the | |
| 108 // `node` - there is a cycle. | |
| 109 errors.add(createError(neighbour.struct, stack)); | |
| 110 break; | |
| 111 case GraphNodeState.VISITED: | |
| 112 // The `neighbour` has already been searched - ignore. | |
| 113 break; | |
| 114 } | |
| 115 } | |
| 116 break; | |
| 117 case GraphNodeState.VISITING: | |
| 118 node.state = GraphNodeState.VISITED; | |
| 119 stack.removeLast(); | |
| 120 break; | |
| 121 case GraphNodeState.VISITED: | |
| 122 // In this case the graph is a DAG. | |
| 123 stack.removeLast(); | |
| 124 break; | |
| 125 } | |
| 126 } | |
| 127 return errors; | |
| 128 } | |
| 129 | |
| 130 CyclicStructError createError(StructNode struct, List<GraphNode> stack) { | |
| 131 LinkedHashSet<StructNode> reversedChain = new LinkedHashSet<StructNode>(); | |
| 132 reversedChain.add(struct); | |
| 133 for (int i = stack.length - 1; i >= 0; --i) { | |
| 134 if (stack[i].state == GraphNodeState.VISITING) { | |
| 135 reversedChain.add(stack[i].struct); | |
| 136 } | |
| 137 if (stack[i].struct == struct) break; | |
| 138 } | |
| 139 | |
| 140 return new CyclicStructError(reversedChain.toList().reversed); | |
| 141 } | |
| 142 } | |
| OLD | NEW |