38 import com.sun.tools.javac.comp.DeferredAttr.AttrMode; |
38 import com.sun.tools.javac.comp.DeferredAttr.AttrMode; |
39 import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; |
39 import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; |
40 import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; |
40 import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; |
41 import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; |
41 import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; |
42 import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; |
42 import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; |
43 |
43 import com.sun.tools.javac.util.GraphUtils.TarjanNode; |
44 import java.util.Comparator; |
44 |
|
45 import java.util.ArrayList; |
|
46 import java.util.Collections; |
|
47 import java.util.EnumMap; |
|
48 import java.util.EnumSet; |
45 import java.util.HashMap; |
49 import java.util.HashMap; |
|
50 import java.util.HashSet; |
|
51 import java.util.LinkedHashSet; |
46 import java.util.Map; |
52 import java.util.Map; |
47 import java.util.Set; |
53 import java.util.Set; |
48 import java.util.TreeSet; |
|
49 |
|
50 import java.util.ArrayList; |
|
51 import java.util.Collections; |
|
52 import java.util.EnumSet; |
|
53 import java.util.HashSet; |
|
54 |
54 |
55 import static com.sun.tools.javac.code.TypeTag.*; |
55 import static com.sun.tools.javac.code.TypeTag.*; |
56 |
56 |
57 /** Helper class for type parameter inference, used by the attribution phase. |
57 /** Helper class for type parameter inference, used by the attribution phase. |
58 * |
58 * |
1067 * tries to select the node that has maximal probability to contain one |
1090 * tries to select the node that has maximal probability to contain one |
1068 * or more inference variables in a given list |
1091 * or more inference variables in a given list |
1069 */ |
1092 */ |
1070 abstract class BestLeafSolver extends LeafSolver { |
1093 abstract class BestLeafSolver extends LeafSolver { |
1071 |
1094 |
|
1095 /** list of ivars of which at least one must be solved */ |
1072 List<Type> varsToSolve; |
1096 List<Type> varsToSolve; |
1073 |
1097 |
1074 BestLeafSolver(List<Type> varsToSolve) { |
1098 BestLeafSolver(List<Type> varsToSolve) { |
1075 this.varsToSolve = varsToSolve; |
1099 this.varsToSolve = varsToSolve; |
1076 } |
1100 } |
1077 |
1101 |
1078 /** |
1102 /** |
1079 * Computes the minimum path that goes from a given node to any of the nodes |
1103 * Computes a path that goes from a given node to the leafs in the graph. |
1080 * containing a variable in {@code varsToSolve}. For any given path, the cost |
1104 * Typically this will start from a node containing a variable in |
1081 * is computed as the total number of type-variables that should be eagerly |
1105 * {@code varsToSolve}. For any given path, the cost is computed as the total |
1082 * instantiated across that path. |
1106 * number of type-variables that should be eagerly instantiated across that path. |
1083 */ |
1107 */ |
1084 int computeMinPath(InferenceGraph g, Node n) { |
1108 Pair<List<Node>, Integer> computeTreeToLeafs(Node n) { |
1085 return computeMinPath(g, n, List.<Node>nil(), 0); |
1109 Pair<List<Node>, Integer> cachedPath = treeCache.get(n); |
1086 } |
1110 if (cachedPath == null) { |
1087 |
1111 //cache miss |
1088 int computeMinPath(InferenceGraph g, Node n, List<Node> path, int cost) { |
1112 if (n.isLeaf()) { |
1089 if (path.contains(n)) return Integer.MAX_VALUE; |
1113 //if leaf, stop |
1090 List<Node> path2 = path.prepend(n); |
1114 cachedPath = new Pair<List<Node>, Integer>(List.of(n), n.data.length()); |
1091 int cost2 = cost + n.data.size(); |
1115 } else { |
1092 if (!Collections.disjoint(n.data, varsToSolve)) { |
1116 //if non-leaf, proceed recursively |
1093 return cost2; |
1117 Pair<List<Node>, Integer> path = new Pair<List<Node>, Integer>(List.of(n), n.data.length()); |
1094 } else { |
1118 for (Node n2 : n.getAllDependencies()) { |
1095 int bestPath = Integer.MAX_VALUE; |
1119 if (n2 == n) continue; |
1096 for (Node n2 : g.nodes) { |
1120 Pair<List<Node>, Integer> subpath = computeTreeToLeafs(n2); |
1097 if (n2.deps.contains(n)) { |
1121 path = new Pair<List<Node>, Integer>( |
1098 int res = computeMinPath(g, n2, path2, cost2); |
1122 path.fst.prependList(subpath.fst), |
1099 if (res < bestPath) { |
1123 path.snd + subpath.snd); |
1100 bestPath = res; |
1124 } |
1101 } |
1125 cachedPath = path; |
1102 } |
1126 } |
1103 } |
1127 //save results in cache |
1104 return bestPath; |
1128 treeCache.put(n, cachedPath); |
1105 } |
1129 } |
1106 } |
1130 return cachedPath; |
|
1131 } |
|
1132 |
|
1133 /** cache used to avoid redundant computation of tree costs */ |
|
1134 final Map<Node, Pair<List<Node>, Integer>> treeCache = |
|
1135 new HashMap<Node, Pair<List<Node>, Integer>>(); |
|
1136 |
|
1137 /** constant value used to mark non-existent paths */ |
|
1138 final Pair<List<Node>, Integer> noPath = |
|
1139 new Pair<List<Node>, Integer>(null, Integer.MAX_VALUE); |
1107 |
1140 |
1108 /** |
1141 /** |
1109 * Pick the leaf that minimize cost |
1142 * Pick the leaf that minimize cost |
1110 */ |
1143 */ |
1111 @Override |
1144 @Override |
1112 public Node pickNode(final InferenceGraph g) { |
1145 public Node pickNode(final InferenceGraph g) { |
1113 final Map<Node, Integer> leavesMap = new HashMap<Node, Integer>(); |
1146 treeCache.clear(); //graph changes at every step - cache must be cleared |
|
1147 Pair<List<Node>, Integer> bestPath = noPath; |
1114 for (Node n : g.nodes) { |
1148 for (Node n : g.nodes) { |
1115 if (n.isLeaf(n)) { |
1149 if (!Collections.disjoint(n.data, varsToSolve)) { |
1116 leavesMap.put(n, computeMinPath(g, n)); |
1150 Pair<List<Node>, Integer> path = computeTreeToLeafs(n); |
1117 } |
1151 //discard all paths containing at least a node in the |
1118 } |
1152 //closure computed above |
1119 Assert.check(!leavesMap.isEmpty(), "No nodes to solve!"); |
1153 if (path.snd < bestPath.snd) { |
1120 TreeSet<Node> orderedLeaves = new TreeSet<Node>(new Comparator<Node>() { |
1154 bestPath = path; |
1121 public int compare(Node n1, Node n2) { |
1155 } |
1122 return leavesMap.get(n1) - leavesMap.get(n2); |
1156 } |
1123 } |
1157 } |
1124 }); |
1158 if (bestPath == noPath) { |
1125 orderedLeaves.addAll(leavesMap.keySet()); |
1159 //no path leads there |
1126 return orderedLeaves.first(); |
1160 throw new NodeNotFoundException(g); |
|
1161 } |
|
1162 return bestPath.fst.head; |
1127 } |
1163 } |
1128 } |
1164 } |
1129 |
1165 |
1130 /** |
1166 /** |
1131 * The inference process can be thought of as a sequence of steps. Each step |
1167 * The inference process can be thought of as a sequence of steps. Each step |
1319 this.steps = steps; |
1355 this.steps = steps; |
1320 } |
1356 } |
1321 } |
1357 } |
1322 |
1358 |
1323 /** |
1359 /** |
|
1360 * There are two kinds of dependencies between inference variables. The basic |
|
1361 * kind of dependency (or bound dependency) arises when a variable mention |
|
1362 * another variable in one of its bounds. There's also a more subtle kind |
|
1363 * of dependency that arises when a variable 'might' lead to better constraints |
|
1364 * on another variable (this is typically the case with variables holding up |
|
1365 * stuck expressions). |
|
1366 */ |
|
1367 enum DependencyKind implements GraphUtils.DependencyKind { |
|
1368 |
|
1369 /** bound dependency */ |
|
1370 BOUND("dotted"), |
|
1371 /** stuck dependency */ |
|
1372 STUCK("dashed"); |
|
1373 |
|
1374 final String dotSyle; |
|
1375 |
|
1376 private DependencyKind(String dotSyle) { |
|
1377 this.dotSyle = dotSyle; |
|
1378 } |
|
1379 |
|
1380 @Override |
|
1381 public String getDotStyle() { |
|
1382 return dotSyle; |
|
1383 } |
|
1384 } |
|
1385 |
|
1386 /** |
1324 * This is the graph inference solver - the solver organizes all inference variables in |
1387 * This is the graph inference solver - the solver organizes all inference variables in |
1325 * a given inference context by bound dependencies - in the general case, such dependencies |
1388 * a given inference context by bound dependencies - in the general case, such dependencies |
1326 * would lead to a cyclic directed graph (hence the name); the dependency info is used to build |
1389 * would lead to a cyclic directed graph (hence the name); the dependency info is used to build |
1327 * an acyclic graph, where all cyclic variables are bundled together. An inference |
1390 * an acyclic graph, where all cyclic variables are bundled together. An inference |
1328 * step corresponds to solving a node in the acyclic graph - this is done by |
1391 * step corresponds to solving a node in the acyclic graph - this is done by |
1329 * relying on a given strategy (see GraphStrategy). |
1392 * relying on a given strategy (see GraphStrategy). |
1330 */ |
1393 */ |
1331 class GraphSolver { |
1394 class GraphSolver { |
1332 |
1395 |
1333 InferenceContext inferenceContext; |
1396 InferenceContext inferenceContext; |
|
1397 Map<Type, Set<Type>> stuckDeps; |
1334 Warner warn; |
1398 Warner warn; |
1335 |
1399 |
1336 GraphSolver(InferenceContext inferenceContext, Warner warn) { |
1400 GraphSolver(InferenceContext inferenceContext, Map<Type, Set<Type>> stuckDeps, Warner warn) { |
1337 this.inferenceContext = inferenceContext; |
1401 this.inferenceContext = inferenceContext; |
|
1402 this.stuckDeps = stuckDeps; |
1338 this.warn = warn; |
1403 this.warn = warn; |
1339 } |
1404 } |
1340 |
1405 |
1341 /** |
1406 /** |
1342 * Solve variables in a given inference context. The amount of variables |
1407 * Solve variables in a given inference context. The amount of variables |
1343 * to be solved, and the way in which the underlying acyclic graph is explored |
1408 * to be solved, and the way in which the underlying acyclic graph is explored |
1344 * depends on the selected solver strategy. |
1409 * depends on the selected solver strategy. |
1345 */ |
1410 */ |
1346 void solve(GraphStrategy sstrategy) { |
1411 void solve(GraphStrategy sstrategy) { |
1347 checkWithinBounds(inferenceContext, warn); //initial propagation of bounds |
1412 checkWithinBounds(inferenceContext, warn); //initial propagation of bounds |
1348 InferenceGraph inferenceGraph = new InferenceGraph(); |
1413 InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps); |
1349 while (!sstrategy.done()) { |
1414 while (!sstrategy.done()) { |
1350 InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph); |
1415 InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph); |
1351 List<Type> varsToSolve = List.from(nodeToSolve.data); |
1416 List<Type> varsToSolve = List.from(nodeToSolve.data); |
1352 List<Type> saved_undet = inferenceContext.save(); |
1417 List<Type> saved_undet = inferenceContext.save(); |
1353 try { |
1418 try { |
1388 * updates on the structure of the graph this node belongs to (used to |
1453 * updates on the structure of the graph this node belongs to (used to |
1389 * keep dependencies in sync). |
1454 * keep dependencies in sync). |
1390 */ |
1455 */ |
1391 class Node extends GraphUtils.TarjanNode<ListBuffer<Type>> { |
1456 class Node extends GraphUtils.TarjanNode<ListBuffer<Type>> { |
1392 |
1457 |
1393 Set<Node> deps; |
1458 /** map listing all dependencies (grouped by kind) */ |
|
1459 EnumMap<DependencyKind, Set<Node>> deps; |
1394 |
1460 |
1395 Node(Type ivar) { |
1461 Node(Type ivar) { |
1396 super(ListBuffer.of(ivar)); |
1462 super(ListBuffer.of(ivar)); |
1397 this.deps = new HashSet<Node>(); |
1463 this.deps = new EnumMap<DependencyKind, Set<Node>>(DependencyKind.class); |
1398 } |
1464 } |
1399 |
1465 |
1400 @Override |
1466 @Override |
1401 public Iterable<? extends Node> getDependencies() { |
1467 public GraphUtils.DependencyKind[] getSupportedDependencyKinds() { |
1402 return deps; |
1468 return DependencyKind.values(); |
1403 } |
1469 } |
1404 |
1470 |
1405 @Override |
1471 @Override |
1406 public String printDependency(GraphUtils.Node<ListBuffer<Type>> to) { |
1472 public String getDependencyName(GraphUtils.Node<ListBuffer<Type>> to, GraphUtils.DependencyKind dk) { |
1407 StringBuilder buf = new StringBuilder(); |
1473 if (dk == DependencyKind.STUCK) return ""; |
1408 String sep = ""; |
1474 else { |
1409 for (Type from : data) { |
1475 StringBuilder buf = new StringBuilder(); |
1410 UndetVar uv = (UndetVar)inferenceContext.asFree(from); |
1476 String sep = ""; |
1411 for (Type bound : uv.getBounds(InferenceBound.values())) { |
1477 for (Type from : data) { |
1412 if (bound.containsAny(List.from(to.data))) { |
1478 UndetVar uv = (UndetVar)inferenceContext.asFree(from); |
1413 buf.append(sep); |
1479 for (Type bound : uv.getBounds(InferenceBound.values())) { |
1414 buf.append(bound); |
1480 if (bound.containsAny(List.from(to.data))) { |
1415 sep = ","; |
1481 buf.append(sep); |
|
1482 buf.append(bound); |
|
1483 sep = ","; |
|
1484 } |
1416 } |
1485 } |
1417 } |
1486 } |
1418 } |
1487 return buf.toString(); |
1419 return buf.toString(); |
1488 } |
1420 } |
1489 } |
1421 |
1490 |
1422 boolean isLeaf(Node n) { |
1491 @Override |
|
1492 public Iterable<? extends Node> getAllDependencies() { |
|
1493 return getDependencies(DependencyKind.values()); |
|
1494 } |
|
1495 |
|
1496 @Override |
|
1497 public Iterable<? extends TarjanNode<ListBuffer<Type>>> getDependenciesByKind(GraphUtils.DependencyKind dk) { |
|
1498 return getDependencies((DependencyKind)dk); |
|
1499 } |
|
1500 |
|
1501 /** |
|
1502 * Retrieves all dependencies with given kind(s). |
|
1503 */ |
|
1504 protected Set<Node> getDependencies(DependencyKind... depKinds) { |
|
1505 Set<Node> buf = new LinkedHashSet<Node>(); |
|
1506 for (DependencyKind dk : depKinds) { |
|
1507 Set<Node> depsByKind = deps.get(dk); |
|
1508 if (depsByKind != null) { |
|
1509 buf.addAll(depsByKind); |
|
1510 } |
|
1511 } |
|
1512 return buf; |
|
1513 } |
|
1514 |
|
1515 /** |
|
1516 * Adds dependency with given kind. |
|
1517 */ |
|
1518 protected void addDependency(DependencyKind dk, Node depToAdd) { |
|
1519 Set<Node> depsByKind = deps.get(dk); |
|
1520 if (depsByKind == null) { |
|
1521 depsByKind = new LinkedHashSet<Node>(); |
|
1522 deps.put(dk, depsByKind); |
|
1523 } |
|
1524 depsByKind.add(depToAdd); |
|
1525 } |
|
1526 |
|
1527 /** |
|
1528 * Add multiple dependencies of same given kind. |
|
1529 */ |
|
1530 protected void addDependencies(DependencyKind dk, Set<Node> depsToAdd) { |
|
1531 for (Node n : depsToAdd) { |
|
1532 addDependency(dk, n); |
|
1533 } |
|
1534 } |
|
1535 |
|
1536 /** |
|
1537 * Remove a dependency, regardless of its kind. |
|
1538 */ |
|
1539 protected Set<DependencyKind> removeDependency(Node n) { |
|
1540 Set<DependencyKind> removedKinds = new HashSet<>(); |
|
1541 for (DependencyKind dk : DependencyKind.values()) { |
|
1542 Set<Node> depsByKind = deps.get(dk); |
|
1543 if (depsByKind == null) continue; |
|
1544 if (depsByKind.remove(n)) { |
|
1545 removedKinds.add(dk); |
|
1546 } |
|
1547 } |
|
1548 return removedKinds; |
|
1549 } |
|
1550 |
|
1551 /** |
|
1552 * Compute closure of a give node, by recursively walking |
|
1553 * through all its dependencies (of given kinds) |
|
1554 */ |
|
1555 protected Set<Node> closure(DependencyKind... depKinds) { |
|
1556 boolean progress = true; |
|
1557 Set<Node> closure = new HashSet<Node>(); |
|
1558 closure.add(this); |
|
1559 while (progress) { |
|
1560 progress = false; |
|
1561 for (Node n1 : new HashSet<Node>(closure)) { |
|
1562 progress = closure.addAll(n1.getDependencies(depKinds)); |
|
1563 } |
|
1564 } |
|
1565 return closure; |
|
1566 } |
|
1567 |
|
1568 /** |
|
1569 * Is this node a leaf? This means either the node has no dependencies, |
|
1570 * or it just has self-dependencies. |
|
1571 */ |
|
1572 protected boolean isLeaf() { |
1423 //no deps, or only one self dep |
1573 //no deps, or only one self dep |
1424 return (n.deps.isEmpty() || |
1574 Set<Node> allDeps = getDependencies(DependencyKind.BOUND, DependencyKind.STUCK); |
1425 n.deps.size() == 1 && n.deps.contains(n)); |
1575 if (allDeps.isEmpty()) return true; |
1426 } |
1576 for (Node n : allDeps) { |
1427 |
1577 if (n != this) { |
1428 void mergeWith(List<? extends Node> nodes) { |
1578 return false; |
|
1579 } |
|
1580 } |
|
1581 return true; |
|
1582 } |
|
1583 |
|
1584 /** |
|
1585 * Merge this node with another node, acquiring its dependencies. |
|
1586 * This routine is used to merge all cyclic node together and |
|
1587 * form an acyclic graph. |
|
1588 */ |
|
1589 protected void mergeWith(List<? extends Node> nodes) { |
1429 for (Node n : nodes) { |
1590 for (Node n : nodes) { |
1430 Assert.check(n.data.length() == 1, "Attempt to merge a compound node!"); |
1591 Assert.check(n.data.length() == 1, "Attempt to merge a compound node!"); |
1431 data.appendList(n.data); |
1592 data.appendList(n.data); |
1432 deps.addAll(n.deps); |
1593 for (DependencyKind dk : DependencyKind.values()) { |
|
1594 addDependencies(dk, n.getDependencies(dk)); |
|
1595 } |
1433 } |
1596 } |
1434 //update deps |
1597 //update deps |
1435 Set<Node> deps2 = new HashSet<Node>(); |
1598 EnumMap<DependencyKind, Set<Node>> deps2 = new EnumMap<DependencyKind, Set<Node>>(DependencyKind.class); |
1436 for (Node d : deps) { |
1599 for (DependencyKind dk : DependencyKind.values()) { |
1437 if (data.contains(d.data.first())) { |
1600 for (Node d : getDependencies(dk)) { |
1438 deps2.add(this); |
1601 Set<Node> depsByKind = deps2.get(dk); |
1439 } else { |
1602 if (depsByKind == null) { |
1440 deps2.add(d); |
1603 depsByKind = new LinkedHashSet<Node>(); |
|
1604 deps2.put(dk, depsByKind); |
|
1605 } |
|
1606 if (data.contains(d.data.first())) { |
|
1607 depsByKind.add(this); |
|
1608 } else { |
|
1609 depsByKind.add(d); |
|
1610 } |
1441 } |
1611 } |
1442 } |
1612 } |
1443 deps = deps2; |
1613 deps = deps2; |
1444 } |
1614 } |
1445 |
1615 |
1446 void graphChanged(Node from, Node to) { |
1616 /** |
1447 if (deps.contains(from)) { |
1617 * Notify all nodes that something has changed in the graph |
1448 deps.remove(from); |
1618 * topology. |
|
1619 */ |
|
1620 private void graphChanged(Node from, Node to) { |
|
1621 for (DependencyKind dk : removeDependency(from)) { |
1449 if (to != null) { |
1622 if (to != null) { |
1450 deps.add(to); |
1623 addDependency(dk, to); |
1451 } |
1624 } |
1452 } |
1625 } |
1453 } |
1626 } |
1454 } |
1627 } |
1455 |
1628 |
1456 /** the nodes in the inference graph */ |
1629 /** the nodes in the inference graph */ |
1457 ArrayList<Node> nodes; |
1630 ArrayList<Node> nodes; |
1458 |
1631 |
1459 InferenceGraph() { |
1632 InferenceGraph(Map<Type, Set<Type>> optDeps) { |
1460 initNodes(); |
1633 initNodes(optDeps); |
|
1634 } |
|
1635 |
|
1636 /** |
|
1637 * Basic lookup helper for retrieving a graph node given an inference |
|
1638 * variable type. |
|
1639 */ |
|
1640 public Node findNode(Type t) { |
|
1641 for (Node n : nodes) { |
|
1642 if (n.data.contains(t)) { |
|
1643 return n; |
|
1644 } |
|
1645 } |
|
1646 return null; |
1461 } |
1647 } |
1462 |
1648 |
1463 /** |
1649 /** |
1464 * Delete a node from the graph. This update the underlying structure |
1650 * Delete a node from the graph. This update the underlying structure |
1465 * of the graph (including dependencies) via listeners updates. |
1651 * of the graph (including dependencies) via listeners updates. |
1482 |
1668 |
1483 /** |
1669 /** |
1484 * Create the graph nodes. First a simple node is created for every inference |
1670 * Create the graph nodes. First a simple node is created for every inference |
1485 * variables to be solved. Then Tarjan is used to found all connected components |
1671 * variables to be solved. Then Tarjan is used to found all connected components |
1486 * in the graph. For each component containing more than one node, a super node is |
1672 * in the graph. For each component containing more than one node, a super node is |
1487 * created, effectively replacing the original cyclic nodes. |
1673 * created, effectively replacing the original cyclic nodes. |
1488 */ |
1674 */ |
1489 void initNodes() { |
1675 void initNodes(Map<Type, Set<Type>> stuckDeps) { |
|
1676 //add nodes |
1490 nodes = new ArrayList<Node>(); |
1677 nodes = new ArrayList<Node>(); |
1491 for (Type t : inferenceContext.restvars()) { |
1678 for (Type t : inferenceContext.restvars()) { |
1492 nodes.add(new Node(t)); |
1679 nodes.add(new Node(t)); |
1493 } |
1680 } |
|
1681 //add dependencies |
1494 for (Node n_i : nodes) { |
1682 for (Node n_i : nodes) { |
1495 Type i = n_i.data.first(); |
1683 Type i = n_i.data.first(); |
|
1684 Set<Type> optDepsByNode = stuckDeps.get(i); |
1496 for (Node n_j : nodes) { |
1685 for (Node n_j : nodes) { |
1497 Type j = n_j.data.first(); |
1686 Type j = n_j.data.first(); |
1498 UndetVar uv_i = (UndetVar)inferenceContext.asFree(i); |
1687 UndetVar uv_i = (UndetVar)inferenceContext.asFree(i); |
1499 if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) { |
1688 if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) { |
1500 //update i's deps |
1689 //update i's bound dependencies |
1501 n_i.deps.add(n_j); |
1690 n_i.addDependency(DependencyKind.BOUND, n_j); |
1502 } |
1691 } |
1503 } |
1692 if (optDepsByNode != null && optDepsByNode.contains(j)) { |
1504 } |
1693 //update i's stuck dependencies |
|
1694 n_i.addDependency(DependencyKind.STUCK, n_j); |
|
1695 } |
|
1696 } |
|
1697 } |
|
1698 //merge cyclic nodes |
1505 ArrayList<Node> acyclicNodes = new ArrayList<Node>(); |
1699 ArrayList<Node> acyclicNodes = new ArrayList<Node>(); |
1506 for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) { |
1700 for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) { |
1507 if (conSubGraph.length() > 1) { |
1701 if (conSubGraph.length() > 1) { |
1508 Node root = conSubGraph.head; |
1702 Node root = conSubGraph.head; |
1509 root.mergeWith(conSubGraph.tail); |
1703 root.mergeWith(conSubGraph.tail); |