public void Serialize_Some_Method() { var code = @" class C { void Foo(string a, string b) { var x = a + b; if (true) { Bar(a, 1); } else { Bar(b, 2); } } void Bar(string a, int x) { } } "; var dot = UcfgSerializer.Serialize(UcfgVerifier.GetUcfgForMethod(code, "Foo")); dot.Should().BeIgnoringLineEndings(@"digraph ""C.Foo(string, string)"" { ENTRY [shape=record label=""{ENTRY|a|b}""] ENTRY -> 0 0 [shape=record label=""{BLOCK:0|\{ \""name\"": \""%0\"" \} __concat a,b|\{ \""name\"": \""x\"" \} __id %0|TERMINATOR JUMP: 1, 2}""] 0 -> 1 0 -> 2 1 [shape=record label=""{BLOCK:1|\{ \""name\"": \""%1\"" \} C.Bar(string, int) _this_,a,\""\""\""\""|TERMINATOR JUMP: 3}""] 1 -> 3 2 [shape=record label=""{BLOCK:2|\{ \""name\"": \""%2\"" \} C.Bar(string, int) _this_,b,\""\""\""\""|TERMINATOR JUMP: 3}""] 2 -> 3 3 [shape=record label=""{BLOCK:3|TERMINATOR RET: \""\""\""\""}""] 3 -> END END [shape=record label=""{END}""] } "); }
public void Serialize_Some_Method() { var code = @" class C { void Foo(string a, string b) { var x = a + b; if (true) { Bar(a, 1); } else { Bar(b, 2); } } void Bar(string a, int x) { } } "; var dot = UcfgSerializer.Serialize(GetUcfgForMethod(code, "Foo")); dot.Should().BeIgnoringLineEndings(@"digraph ""C.Foo(string, string)"" { ENTRY [shape=record label=""{ENTRY|a|b}""] ENTRY -> 0 0 [shape=record label=""{BLOCK|%0 __concat b,a|x __id %0}""] 0 -> 1 0 -> 2 1 [shape=record label=""{BLOCK|%0 C.Bar(string, int) a,CONST}""] 1 -> 3 2 [shape=record label=""{BLOCK|%0 C.Bar(string, int) b,CONST}""] 2 -> 3 3 [shape=record label=""{BLOCK}""] 3 -> EXIT EXIT [shape=record label=""{EXIT}""] } "); }
public void Serialize_Some_Method() { var code = @" class C { void Foo(string a, string b) { var x = a + b; if (true) { Bar(a, 1); } else { Bar(b, 2); } } void Bar(string a, int x) { } } "; var dot = UcfgSerializer.Serialize(UcfgVerifier.GetUcfgForMethod(code, "Foo")); dot.Should().BeIgnoringLineEndings(@"digraph ""C.Foo(string, string)"" { ENTRY [shape=record label=""{ENTRY|a|b}""] ENTRY -> 0 0 [shape=record label=""{BLOCK:#0|%0 := __concat [ a, b ]|x := __id [ %0 ]|TERMINATOR: JUMP: #1, #2}""] 0 -> 1 0 -> 2 1 [shape=record label=""{BLOCK:#1|%1 := C.Bar(string, int) [ this, a, CONST ]|TERMINATOR: JUMP: #3}""] 1 -> 3 2 [shape=record label=""{BLOCK:#2|%2 := C.Bar(string, int) [ this, b, CONST ]|TERMINATOR: JUMP: #3}""] 2 -> 3 3 [shape=record label=""{BLOCK:#3|TERMINATOR: RET: CONST}""] 3 -> END END [shape=record label=""{END}""] } "); }
private void WriteUCFG <TDeclarationSyntax>(SyntaxNodeAnalysisContext context, Func <TDeclarationSyntax, CSharpSyntaxNode> getBody) where TDeclarationSyntax : SyntaxNode { var declaration = (TDeclarationSyntax)context.Node; var symbol = context.SemanticModel.GetDeclaredSymbol(declaration); var methodSymbol = (symbol is IPropertySymbol propertySymbol) ? propertySymbol.GetMethod // We are in PropertyDeclarationSyntax : symbol as IMethodSymbol; // all other are methods if (methodSymbol == null || methodSymbol.IsAbstract || methodSymbol.IsExtern || !CSharpControlFlowGraph.TryGet(getBody(declaration), context.SemanticModel, out var cfg)) { return; } var ucfg = new UniversalControlFlowGraphBuilder() .Build(context.SemanticModel, declaration, methodSymbol, cfg); if (IsValid(ucfg)) { var fileName = $"{projectBuildId}_{Interlocked.Increment(ref protobufFileIndex)}"; WriteProtobuf(ucfg, Path.Combine(protobufDirectory, $"ucfg_{fileName}.pb")); if (ShouldGenerateDot) { WriteDot(Path.Combine(protobufDirectory, $"ucfg_{fileName}.dot"), writer => UcfgSerializer.Serialize(ucfg, writer)); WriteDot(Path.Combine(protobufDirectory, $"cfg_{fileName}.dot"), writer => CfgSerializer.Serialize(ucfg.MethodId, cfg, writer)); } } }
private void WriteUcfg <TDeclarationSyntax>(SyntaxNodeAnalysisContext context, Func <TDeclarationSyntax, CSharpSyntaxNode> getBody) where TDeclarationSyntax : SyntaxNode { var declaration = (TDeclarationSyntax)context.Node; var symbol = context.SemanticModel.GetDeclaredSymbol(declaration); var methodSymbol = (symbol is IPropertySymbol propertySymbol) ? propertySymbol.GetMethod // We are in PropertyDeclarationSyntax : symbol as IMethodSymbol; // all other are methods if (methodSymbol == null || methodSymbol.IsAbstract || methodSymbol.IsExtern || !CSharpControlFlowGraph.TryGet(getBody(declaration), context.SemanticModel, out var cfg)) { return; } try { var ucfg = new UcfgFactory(context.SemanticModel) .Create(declaration, methodSymbol, cfg); if (!IsValid(ucfg)) { return; } var fileName = $"{this.projectBuildId}_{Interlocked.Increment(ref this.protobufFileIndex)}"; WriteProtobuf(ucfg, Path.Combine(this.protobufDirectory, $"ucfg_{fileName}.pb")); if (ShouldGenerateDot) { WriteDot(Path.Combine(this.protobufDirectory, $"ucfg_{fileName}.dot"), writer => UcfgSerializer.Serialize(ucfg, writer)); WriteDot(Path.Combine(this.protobufDirectory, $"cfg_{fileName}.dot"), writer => CfgSerializer.Serialize(ucfg.MethodId, cfg, writer)); } } catch (UcfgException) when(!DebugHelper.IsInternalDebuggingContext()) { // Ignore the exception in production } }