/// <summary> /// Applies all edits to the control flow graph. /// </summary> /// <param name="graph">The graph to apply the transaction on.</param> /// <exception cref="InvalidOperationException">Occurs when the transaction was already applied.</exception> /// <remarks> /// When an exception occurs within one of the edits, all edits previously applied will be reverted. /// </remarks> public void Apply(ControlFlowGraph <TInstruction> graph) { if (IsCompleted) { throw new InvalidOperationException("The transaction is already applied."); } var context = new ControlFlowGraphEditContext <TInstruction>(graph); int index = 0; try { for (; index < _actions.Count; index++) { var edit = _actions[index]; edit.Apply(context); } IsCompleted = true; } catch { index--; for (; index >= 0; index--) { _actions[index].Revert(context); } throw; } }
/// <inheritdoc /> protected override void OnRevert(ControlFlowGraphEditContext <TInstruction> context) { var origin = context.FindNode(OriginOffset); var target = context.FindNodeOrSplit(TargetOffset, out _); origin.ConnectWith(target, EdgeType); }
/// <inheritdoc /> public void Revert(ControlFlowGraphEditContext <TInstruction> context) { if (!_isApplied) { throw new InvalidOperationException("Operation is not applied yet."); } var node = context.FindNode(BranchOffset); // Re-merge node if it was split. if (_hasSplit) { var newNeighbour = node.FallThroughNeighbour; node.FallThroughNeighbour = null; context.RemoveNodeFromIndex(newNeighbour.Offset); newNeighbour.MergeWithPredecessor(); } // Restore original fallthrough neighbour. node.FallThroughNeighbour = _oldFallThroughOffset.HasValue ? context.FindNode(_oldFallThroughOffset.Value) : null; _isApplied = false; }
/// <inheritdoc /> public void Apply(ControlFlowGraphEditContext <TInstruction> context) { if (_isApplied) { throw new InvalidOperationException("Operation is already applied."); } context.FindNodeOrSplit(SplitOffset, out _hasSplit); _isApplied = true; }
/// <inheritdoc /> public void Revert(ControlFlowGraphEditContext <TInstruction> context) { if (!_isApplied) { throw new InvalidOperationException("Operation is not applied yet."); } OnRevert(context); _isApplied = false; }
/// <inheritdoc /> public void Apply(ControlFlowGraphEditContext <TInstruction> context) { if (_isApplied) { throw new InvalidOperationException("Operation is already applied."); } OnApply(context); _isApplied = true; }
/// <inheritdoc /> public void Revert(ControlFlowGraphEditContext <TInstruction> context) { if (!_isApplied) { throw new InvalidOperationException("Operation is not applied yet."); } if (_hasSplit) { var node = context.Graph.Nodes[SplitOffset]; context.RemoveNodeFromIndex(SplitOffset); node.MergeWithPredecessor(); } }
/// <inheritdoc /> public void Apply(ControlFlowGraphEditContext <TInstruction> context) { if (_isApplied) { throw new InvalidOperationException("Operation is already applied."); } var node = context.FindNode(BranchOffset); // Save original fallthrough node offset. _oldFallThroughOffset = node.FallThroughNeighbour?.Offset; // Set new fallthrough neighbour. node.FallThroughNeighbour = NewFallThroughOffset.HasValue ? context.FindNodeOrSplit(NewFallThroughOffset.Value, out _hasSplit) : null; _isApplied = true; }
/// <inheritdoc /> protected override void OnRevert(ControlFlowGraphEditContext <TInstruction> context) { var origin = context.FindNode(OriginOffset); var target = context.Graph.Nodes[TargetOffset]; var collection = EdgeType switch { ControlFlowEdgeType.Conditional => origin.ConditionalEdges, ControlFlowEdgeType.Abnormal => origin.AbnormalEdges, _ => throw new ArgumentOutOfRangeException() }; collection.Remove(collection.GetEdgesToNeighbour(target).First()); if (_hasSplit) { context.RemoveNodeFromIndex(target.Offset); target.MergeWithPredecessor(); } }
/// <inheritdoc /> protected override void OnApply(ControlFlowGraphEditContext <TInstruction> context) { var origin = context.FindNode(OriginOffset); var target = context.Graph.Nodes[TargetOffset]; var collection = EdgeType switch { ControlFlowEdgeType.Conditional => origin.ConditionalEdges, ControlFlowEdgeType.Abnormal => origin.AbnormalEdges, _ => throw new ArgumentOutOfRangeException() }; collection.Remove(collection.GetEdgesToNeighbour(target).First()); var incomingEdges = target.GetIncomingEdges().ToArray(); if (incomingEdges.Length == 1 && incomingEdges[0].Type == ControlFlowEdgeType.FallThrough) { context.RemoveNodeFromIndex(target.Offset); target.MergeWithPredecessor(); } }
/// <summary> /// Reverts the update to the adjacency list of the node. /// </summary> /// <param name="context">The editing context.</param> /// <remarks> /// This method is guaranteed to be called after <see cref="Apply"/>. /// </remarks> protected abstract void OnRevert(ControlFlowGraphEditContext <TInstruction> context);