497 return node;
498 }
499
500 /**
501 * The type of events sent to a {@link NodeEventListener}.
502 */
503 public enum NodeEvent {
504 /**
505 * A node's input is changed.
506 */
507 INPUT_CHANGED,
508
509 /**
510 * A node's {@linkplain Node#usages() usages} count dropped to zero.
511 */
512 ZERO_USAGES,
513
514 /**
515 * A node was added to a graph.
516 */
517 NODE_ADDED;
518 }
519
520 /**
521 * Client interested in one or more node related events.
522 */
523 public interface NodeEventListener {
524
525 /**
526 * Default handler for events.
527 *
528 * @param e an event
529 * @param node the node related to {@code e}
530 */
531 default void event(NodeEvent e, Node node) {
532 }
533
534 /**
535 * Notifies this listener of a change in a node's inputs.
536 *
537 * @param node a node who has had one of its inputs changed
538 */
539 default void inputChanged(Node node) {
540 event(NodeEvent.INPUT_CHANGED, node);
541 }
542
543 /**
544 * Notifies this listener of a node becoming unused.
545 *
546 * @param node a node whose {@link Node#usages()} just became empty
547 */
548 default void usagesDroppedToZero(Node node) {
549 event(NodeEvent.ZERO_USAGES, node);
550 }
551
552 /**
553 * Notifies this listener of an added node.
554 *
555 * @param node a node that was just added to the graph
556 */
557 default void nodeAdded(Node node) {
558 event(NodeEvent.NODE_ADDED, node);
559 }
560 }
561
562 /**
563 * Registers a given {@link NodeEventListener} with the enclosing graph until this object is
564 * {@linkplain #close() closed}.
565 */
566 public final class NodeEventScope implements AutoCloseable {
567 NodeEventScope(NodeEventListener listener) {
568 if (nodeEventListener == null) {
569 nodeEventListener = listener;
570 } else {
571 nodeEventListener = new ChainedNodeEventListener(listener, nodeEventListener);
572 }
573 }
574
575 @Override
576 public void close() {
577 assert nodeEventListener != null;
578 if (nodeEventListener instanceof ChainedNodeEventListener) {
579 nodeEventListener = ((ChainedNodeEventListener) nodeEventListener).next;
580 } else {
581 nodeEventListener = null;
582 }
583 }
584 }
585
586 private static class ChainedNodeEventListener implements NodeEventListener {
587
588 NodeEventListener head;
589 NodeEventListener next;
590
591 ChainedNodeEventListener(NodeEventListener head, NodeEventListener next) {
592 this.head = head;
593 this.next = next;
594 }
595
596 @Override
597 public void nodeAdded(Node node) {
598 head.nodeAdded(node);
599 next.nodeAdded(node);
600 }
601
602 @Override
603 public void inputChanged(Node node) {
604 head.inputChanged(node);
605 next.inputChanged(node);
606 }
607
608 @Override
609 public void usagesDroppedToZero(Node node) {
610 head.usagesDroppedToZero(node);
611 next.usagesDroppedToZero(node);
612 }
613 }
614
615 /**
616 * Registers a given {@link NodeEventListener} with this graph. This should be used in
617 * conjunction with try-with-resources statement as follows:
618 *
619 * <pre>
620 * try (NodeEventScope nes = graph.trackNodeEvents(listener)) {
621 * // make changes to the graph
622 * }
623 * </pre>
624 */
625 public NodeEventScope trackNodeEvents(NodeEventListener listener) {
626 return new NodeEventScope(listener);
627 }
628
629 /**
630 * Looks for a node <i>similar</i> to {@code node} and returns it if found. Otherwise
631 * {@code node} is added to this graph and returned.
1006 return new NodeWorkList.IterativeNodeWorkList(this, fill, iterationLimitPerNode);
1007 }
1008
1009 void register(Node node) {
1010 assert !isFrozen();
1011 assert node.id() == Node.INITIAL_ID;
1012 if (nodes.length == nodesSize) {
1013 grow();
1014 }
1015 int id = nodesSize++;
1016 nodes[id] = node;
1017 node.id = id;
1018 if (currentNodeSourcePosition != null) {
1019 node.setNodeSourcePosition(currentNodeSourcePosition);
1020 }
1021 seenNodeSourcePosition = seenNodeSourcePosition || node.getNodeSourcePosition() != null;
1022
1023 updateNodeCaches(node);
1024
1025 if (nodeEventListener != null) {
1026 nodeEventListener.nodeAdded(node);
1027 }
1028 afterRegister(node);
1029 }
1030
1031 private void grow() {
1032 Node[] newNodes = new Node[(nodesSize * 2) + 1];
1033 System.arraycopy(nodes, 0, newNodes, 0, nodesSize);
1034 nodes = newNodes;
1035 }
1036
1037 @SuppressWarnings("unused")
1038 protected void afterRegister(Node node) {
1039
1040 }
1041
1042 @SuppressWarnings("unused")
1043 private void postDeserialization() {
1044 recomputeIterableNodeLists();
1045 }
1046
1067 iterableNodesLast.add(null);
1068 }
1069 Node prev = iterableNodesLast.get(nodeClassId);
1070 if (prev != null) {
1071 prev.typeCacheNext = node;
1072 } else {
1073 iterableNodesFirst.set(nodeClassId, node);
1074 }
1075 iterableNodesLast.set(nodeClassId, node);
1076 }
1077 }
1078
1079 void unregister(Node node) {
1080 assert !isFrozen();
1081 assert !node.isDeleted() : node;
1082 if (node.getNodeClass().isLeafNode() && node.getNodeClass().valueNumberable()) {
1083 removeNodeFromCache(node);
1084 }
1085 nodes[node.id] = null;
1086 nodesDeletedSinceLastCompression++;
1087
1088 // nodes aren't removed from the type cache here - they will be removed during iteration
1089 }
1090
1091 public boolean verify() {
1092 if (Options.VerifyGraalGraphs.getValue(options)) {
1093 for (Node node : getNodes()) {
1094 try {
1095 try {
1096 assert node.verify();
1097 } catch (AssertionError t) {
1098 throw new GraalError(t);
1099 } catch (RuntimeException t) {
1100 throw new GraalError(t);
1101 }
1102 } catch (GraalError e) {
1103 throw GraalGraphError.transformAndAddContext(e, node).addContext(this);
1104 }
1105 }
1106 }
|
497 return node;
498 }
499
500 /**
501 * The type of events sent to a {@link NodeEventListener}.
502 */
503 public enum NodeEvent {
504 /**
505 * A node's input is changed.
506 */
507 INPUT_CHANGED,
508
509 /**
510 * A node's {@linkplain Node#usages() usages} count dropped to zero.
511 */
512 ZERO_USAGES,
513
514 /**
515 * A node was added to a graph.
516 */
517 NODE_ADDED,
518
519 /**
520 * A node was removed from the graph.
521 */
522 NODE_REMOVED;
523 }
524
525 /**
526 * Client interested in one or more node related events.
527 */
528 public abstract static class NodeEventListener {
529
530 /**
531 * A method called when a change event occurs.
532 *
533 * This method dispatches the event to user-defined triggers. The methods that change the
534 * graph (typically in Graph and Node) must call this method to dispatch the event.
535 *
536 * @param e an event
537 * @param node the node related to {@code e}
538 */
539 final void event(NodeEvent e, Node node) {
540 switch (e) {
541 case INPUT_CHANGED:
542 inputChanged(node);
543 break;
544 case ZERO_USAGES:
545 usagesDroppedToZero(node);
546 break;
547 case NODE_ADDED:
548 nodeAdded(node);
549 break;
550 case NODE_REMOVED:
551 nodeRemoved(node);
552 break;
553 }
554 changed(e, node);
555 }
556
557 /**
558 * Notifies this listener about any change event in the graph.
559 *
560 * @param e an event
561 * @param node the node related to {@code e}
562 */
563 public void changed(NodeEvent e, Node node) {
564 }
565
566 /**
567 * Notifies this listener about a change in a node's inputs.
568 *
569 * @param node a node who has had one of its inputs changed
570 */
571 public void inputChanged(Node node) {
572 }
573
574 /**
575 * Notifies this listener of a node becoming unused.
576 *
577 * @param node a node whose {@link Node#usages()} just became empty
578 */
579 public void usagesDroppedToZero(Node node) {
580 }
581
582 /**
583 * Notifies this listener of an added node.
584 *
585 * @param node a node that was just added to the graph
586 */
587 public void nodeAdded(Node node) {
588 }
589
590 /**
591 * Notifies this listener of a removed node.
592 *
593 * @param node
594 */
595 public void nodeRemoved(Node node) {
596 }
597 }
598
599 /**
600 * Registers a given {@link NodeEventListener} with the enclosing graph until this object is
601 * {@linkplain #close() closed}.
602 */
603 public final class NodeEventScope implements AutoCloseable {
604 NodeEventScope(NodeEventListener listener) {
605 if (nodeEventListener == null) {
606 nodeEventListener = listener;
607 } else {
608 nodeEventListener = new ChainedNodeEventListener(listener, nodeEventListener);
609 }
610 }
611
612 @Override
613 public void close() {
614 assert nodeEventListener != null;
615 if (nodeEventListener instanceof ChainedNodeEventListener) {
616 nodeEventListener = ((ChainedNodeEventListener) nodeEventListener).next;
617 } else {
618 nodeEventListener = null;
619 }
620 }
621 }
622
623 private static class ChainedNodeEventListener extends NodeEventListener {
624
625 NodeEventListener head;
626 NodeEventListener next;
627
628 ChainedNodeEventListener(NodeEventListener head, NodeEventListener next) {
629 this.head = head;
630 this.next = next;
631 }
632
633 @Override
634 public void nodeAdded(Node node) {
635 head.event(NodeEvent.NODE_ADDED, node);
636 next.event(NodeEvent.NODE_ADDED, node);
637 }
638
639 @Override
640 public void inputChanged(Node node) {
641 head.event(NodeEvent.INPUT_CHANGED, node);
642 next.event(NodeEvent.INPUT_CHANGED, node);
643 }
644
645 @Override
646 public void usagesDroppedToZero(Node node) {
647 head.event(NodeEvent.ZERO_USAGES, node);
648 next.event(NodeEvent.ZERO_USAGES, node);
649 }
650
651 @Override
652 public void nodeRemoved(Node node) {
653 head.event(NodeEvent.NODE_REMOVED, node);
654 next.event(NodeEvent.NODE_REMOVED, node);
655 }
656
657 @Override
658 public void changed(NodeEvent e, Node node) {
659 head.event(e, node);
660 next.event(e, node);
661 }
662 }
663
664 /**
665 * Registers a given {@link NodeEventListener} with this graph. This should be used in
666 * conjunction with try-with-resources statement as follows:
667 *
668 * <pre>
669 * try (NodeEventScope nes = graph.trackNodeEvents(listener)) {
670 * // make changes to the graph
671 * }
672 * </pre>
673 */
674 public NodeEventScope trackNodeEvents(NodeEventListener listener) {
675 return new NodeEventScope(listener);
676 }
677
678 /**
679 * Looks for a node <i>similar</i> to {@code node} and returns it if found. Otherwise
680 * {@code node} is added to this graph and returned.
1055 return new NodeWorkList.IterativeNodeWorkList(this, fill, iterationLimitPerNode);
1056 }
1057
1058 void register(Node node) {
1059 assert !isFrozen();
1060 assert node.id() == Node.INITIAL_ID;
1061 if (nodes.length == nodesSize) {
1062 grow();
1063 }
1064 int id = nodesSize++;
1065 nodes[id] = node;
1066 node.id = id;
1067 if (currentNodeSourcePosition != null) {
1068 node.setNodeSourcePosition(currentNodeSourcePosition);
1069 }
1070 seenNodeSourcePosition = seenNodeSourcePosition || node.getNodeSourcePosition() != null;
1071
1072 updateNodeCaches(node);
1073
1074 if (nodeEventListener != null) {
1075 nodeEventListener.event(NodeEvent.NODE_ADDED, node);
1076 }
1077 afterRegister(node);
1078 }
1079
1080 private void grow() {
1081 Node[] newNodes = new Node[(nodesSize * 2) + 1];
1082 System.arraycopy(nodes, 0, newNodes, 0, nodesSize);
1083 nodes = newNodes;
1084 }
1085
1086 @SuppressWarnings("unused")
1087 protected void afterRegister(Node node) {
1088
1089 }
1090
1091 @SuppressWarnings("unused")
1092 private void postDeserialization() {
1093 recomputeIterableNodeLists();
1094 }
1095
1116 iterableNodesLast.add(null);
1117 }
1118 Node prev = iterableNodesLast.get(nodeClassId);
1119 if (prev != null) {
1120 prev.typeCacheNext = node;
1121 } else {
1122 iterableNodesFirst.set(nodeClassId, node);
1123 }
1124 iterableNodesLast.set(nodeClassId, node);
1125 }
1126 }
1127
1128 void unregister(Node node) {
1129 assert !isFrozen();
1130 assert !node.isDeleted() : node;
1131 if (node.getNodeClass().isLeafNode() && node.getNodeClass().valueNumberable()) {
1132 removeNodeFromCache(node);
1133 }
1134 nodes[node.id] = null;
1135 nodesDeletedSinceLastCompression++;
1136
1137 if (nodeEventListener != null) {
1138 nodeEventListener.event(NodeEvent.NODE_ADDED, node);
1139 }
1140
1141 // nodes aren't removed from the type cache here - they will be removed during iteration
1142 }
1143
1144 public boolean verify() {
1145 if (Options.VerifyGraalGraphs.getValue(options)) {
1146 for (Node node : getNodes()) {
1147 try {
1148 try {
1149 assert node.verify();
1150 } catch (AssertionError t) {
1151 throw new GraalError(t);
1152 } catch (RuntimeException t) {
1153 throw new GraalError(t);
1154 }
1155 } catch (GraalError e) {
1156 throw GraalGraphError.transformAndAddContext(e, node).addContext(this);
1157 }
1158 }
1159 }
|