[NotNull] private static IReadOnlyDictionary <IBasicBlock, IMutableBasicBlock> CloneVertices( [NotNull] this IControlFlowGraph input, IMutableControlFlowGraph output, [NotNull] Func <IBasicBlock, bool> keep, [CanBeNull] Action <IBasicBlock, IMutableBasicBlock> copy = null ) { // Clone vertices (without edges) var replacements = new Dictionary <IBasicBlock, IMutableBasicBlock>(); foreach (var vertex in input.Vertices.Where(keep)) { var r = output.CreateNewBlock(vertex.Type, vertex.LineNumber, vertex.ID); replacements.Add(vertex, r); if (copy != null) { copy(vertex, r); } else { foreach (var stmt in vertex.Statements) { r.Add(stmt); } } } return(replacements); }
private void HandleGoto(IMutableControlFlowGraph cfg, Goto @goto, IMutableBasicBlock block, int lineNumber) { block.Add(@goto); if (@goto.Destination.IsConstant) { var dest = @goto.Destination.StaticEvaluate(); if (dest.Type == Execution.Type.Number) { // We know exactly where this is going, jump to that line var line = Math.Clamp((int)dest.Number, 1, _maxLines); var destBlock = GetLineEntryBlock(cfg, line); cfg.CreateEdge(block, destBlock, EdgeType.GotoConstNum); } else if (dest.Type == Execution.Type.String) { // We tried to statically jump to a string (which is always an error), fallthrough to the next line AddFallthrough(cfg, block, lineNumber, EdgeType.GotoConstStr); } } else { // We don't know where this is going, so goto every line for (var j = 1; j <= _maxLines; j++) { cfg.CreateEdge(block, GetLineEntryBlock(cfg, j), EdgeType.GotoExpression); } } }
private IMutableBasicBlock GetLineEntryBlock(IMutableControlFlowGraph cfg, int lineNumber) { if (!_lineStartBlocks.TryGetValue(lineNumber, out var block)) { block = cfg.CreateNewBlock(BasicBlockType.LineStart, lineNumber); _lineStartBlocks.Add(lineNumber, block); } return(block); }
private static IReadOnlyDictionary <IEdge, IEdge> CloneEdges( [NotNull] this IControlFlowGraph input, IMutableControlFlowGraph output, IReadOnlyDictionary <IBasicBlock, IMutableBasicBlock> vertexReplacements, [NotNull] Func <IEdge, bool> keep) { var replacements = new Dictionary <IEdge, IEdge>(); foreach (var edge in input.Edges) { if (!keep(edge)) { continue; } replacements.Add( edge, output.CreateEdge(vertexReplacements[edge.Start], vertexReplacements[edge.End], edge.Type) ); } return(replacements); }
/// <summary> /// Convert a statement list into basic blocks. Return the final block that was created if it needs to be connected to the next block /// </summary> /// <param name="cfg"></param> /// <param name="statements"></param> /// <param name="lineNumber"></param> /// <param name="entry"></param> /// <returns>(entry_block!, exit_block?)</returns> private (IMutableBasicBlock, IMutableBasicBlock) HandleStatementList(IMutableControlFlowGraph cfg, StatementList statements, int lineNumber, IMutableBasicBlock entry) { var block = cfg.CreateNewBlock(BasicBlockType.Basic, lineNumber); cfg.CreateEdge(entry, block, EdgeType.Continue); foreach (var stmt in statements.Statements) { if (stmt is Goto @goto) { HandleGoto(cfg, @goto, block, lineNumber); // Create a new block which is _not_ linked to the previous (because we just unconditionally jumped away) block = cfg.CreateNewBlock(BasicBlockType.Basic, lineNumber); } else if (stmt is If @if) { block.Add(new Conditional(@if.Condition)); // Convert true and false branches into blocks var(enTrue, exTrue) = HandleStatementList(cfg, @if.TrueBranch, lineNumber, cfg.CreateNewBlock(BasicBlockType.Basic, lineNumber)); var(enFals, exFals) = HandleStatementList(cfg, @if.FalseBranch, lineNumber, cfg.CreateNewBlock(BasicBlockType.Basic, lineNumber)); // Create edges from conditional to two branches cfg.CreateEdge(block, enTrue, EdgeType.ConditionalTrue); cfg.CreateEdge(block, enFals, EdgeType.ConditionalFalse); // Link the exit blocks (if they're not null) to the next block block = cfg.CreateNewBlock(BasicBlockType.Basic, lineNumber); if (exTrue != null) { cfg.CreateEdge(exTrue, block, EdgeType.Continue); } if (exFals != null) { cfg.CreateEdge(exFals, block, EdgeType.Continue); } } else if (stmt is EmptyStatement) { // ignore empty statements, they (obviously) don't do anything // ReSharper disable once RedundantJumpStatement continue; } else { // Add to block with fallthrough to next line if it can error block.Add(stmt); if (stmt.CanRuntimeError) { AddFallthrough(cfg, block, lineNumber, EdgeType.RuntimeError); var b2 = cfg.CreateNewBlock(BasicBlockType.Basic, block.LineNumber); cfg.CreateEdge(block, b2, EdgeType.Continue); block = b2; } } } return(entry, block); }
private void AddFallthrough(IMutableControlFlowGraph cfg, IBasicBlock source, int currentLineNumber, EdgeType type = EdgeType.Continue) { cfg.CreateEdge(source, GetLineEntryBlock(cfg, currentLineNumber == _maxLines ? 1 : currentLineNumber + 1), type); }