public void GetLocation_Multiline_Invocation_StartAtZero_EndAtOne() { const string code = @" // 1 - SQ line numbers public class Class1 // 2 { // 3 public string Foo(string s) // 4 { // 5 var x = // 6 %0 = string.Trim(s) --> SL = 7, SLO = 0, EL = 8, ELO = 1 s.Trim( // 7 x = __id(%0) --> SL = 6, SLO = 12, EL = 8, ELO = 1 ); // 8 // 9 // 10 return x; // 11 //23456789012345678901234567890 // SQ column offsets } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); var block = ucfg.BasicBlocks[0]; // Block locations are not used by the Security engine ucfg.BasicBlocks[0].Location.Should().BeNull(); AssertLocation(ucfg.BasicBlocks[0].Instructions[0].Assigncall.Location, startLine: 7, startLineOffset: 0, endLine: 8, endLineOffset: 1); AssertLocation(ucfg.BasicBlocks[0].Instructions[1].Assigncall.Location, startLine: 6, startLineOffset: 12, endLine: 8, endLineOffset: 1); }
public void SpecialBlocks_Goto() { const string code = @" public class Class1 { public void Foo(int x) { switch (x) // Branch(Jump0,Jump1,Exit) | Basic#0(Jump:#1,#2,#3) { case 0: // Jump0(Jump1) | Basic#1(Jump:#2) goto case 1; case 1: // Jump1(Exit) | Basic#2(Jump:#3) break; } } // Exit | Basic#3(Ret) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1", "2", "3" }), b => ValidateJmpBlock(b, expectedId: "2", expectedJumps: new[] { "3" }), b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: new[] { "2" }), b => ValidateRetBlock(b, expectedId: "3", expectedReturnExpression: ConstValue) ); }
public void GetLocation_Returns_1Based_Line_0Based_LineOffset() { const string code = @" // 1 - SQ line numbers public class Class1 // 2 { // 3 public string Foo(string s) // 4 { // 5 var x = s.Trim(); // 6 %0 = string.Trim(s) --> SL = 6, SLO = 16, EL = 6, ELO = 24 // 7 x = __id(%0) --> SL = 6, SLO = 12, EL = 6, ELO = 24 return x; // 8 //23456789012345678901234567890 // SQ column offsets } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); var block = ucfg.BasicBlocks[0]; // Block locations are not used by the Security engine ucfg.BasicBlocks[0].Location.Should().BeNull(); AssertLocation(ucfg.BasicBlocks[0].Instructions[0].Assigncall.Location, startLine: 6, startLineOffset: 16, endLine: 6, endLineOffset: 24); AssertLocation(ucfg.BasicBlocks[0].Instructions[1].Assigncall.Location, startLine: 6, startLineOffset: 12, endLine: 6, endLineOffset: 24); }
public void Void_Method_Branch2() { const string code = @" using System; public class Class1 { public void Foo(string s) { if (true) // Branch (Next:Jump,Exit) | Block#0(Jump:#1,#2) return; // Jump (Next:Exit) | Block#1(Ret) Console.WriteLine(s); // Simple (Next:Exit) | Block#2(Jump:#3) // Exit | Block#3(Ret) } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); ucfg.Entries.Should().BeEquivalentTo(new[] { "0" }); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1", "2" }), b => ValidateRetBlock(b, expectedId: "1", expectedReturnExpression: ConstValue), b => ValidateJmpBlock(b, expectedId: "2", expectedJumps: "3"), b => ValidateRetBlock(b, expectedId: "3", expectedReturnExpression: ConstValue) ); }
public void ConstantExpressions_Share_The_Same_Instance() { const string code = @" public class Class1 { private string field; public void Foo(string s) { string a = ""a""; // a := __id [ const ] string b = ""b""; // b := __id [ const ] string c = ""c""; // c := __id [ const ] } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); var a = ucfg.BasicBlocks[0].Instructions[0].Assigncall.Args[0]; var b = ucfg.BasicBlocks[0].Instructions[1].Assigncall.Args[0]; var c = ucfg.BasicBlocks[0].Instructions[2].Assigncall.Args[0]; // The constant expressions share the same instance of the Const value // for performance and simplicity. The protobuf serializer will deserialize // the values as a singleton again. a.Should().Be(b); a.Should().Be(c); }
public void EntryPointMethod_Has_Additional_Block() { const string code = @" using System.Web.Mvc; public class Class1 : Controller { public void Foo(string s) { // | Basic#1(Jump:#0) - contains entrypoint instruction and attributes } // Exit | Basic#0(Ret) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); ucfg.Entries.Should().BeEquivalentTo(new[] { "1" }); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateRetBlock(b, expectedId: "0", expectedReturnExpression: ConstValue), // block from cfg b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: new[] { "0" }) // fake entrypoint block ); }
public void SpecialBlocks_Lock() { const string code = @" public class Class1 { public void Foo(object o, string s) { lock (o) // Lock(Exit) | Basic#0(Jump:#1) { } } // Exit | Basic#1(Ret) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1" }), b => ValidateRetBlock(b, expectedId: "1", expectedReturnExpression: ConstValue) ); }
public void String_Method_Simple() { const string code = @" public class Class1 { public string Foo(string s) { return s; // Jump (Next:Exit) | Block#0(Ret:s) // Exit | Block#1(Ret:Const)// ignored when deserializing } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); ucfg.Entries.Should().BeEquivalentTo(new[] { "0" }); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateRetBlock(b, expectedId: "0", expectedReturnExpression: "s"), b => ValidateRetBlock(b, expectedId: "1", expectedReturnExpression: ConstValue)); }
public void SpecialBlocks_Foreach() { const string code = @" public class Class1 { public void Foo(string[] items) { foreach (var item in items) // Foreach(BinaryBranch) | Basic#0 // BinaryBranch(BinaryBranch,Exit) | Basic#1(Jump:#1,#2) { } } // Exit | Basic#2(Ret) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1" }), b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: new[] { "1", "2" }), b => ValidateRetBlock(b, expectedId: "2", expectedReturnExpression: ConstValue) ); }
public void SpecialBlocks_For() { const string code = @" public class Class1 { public void Foo(string[] items) { for (int i = 0; i < items.Length; i++) // For(BinaryBranch) | Basic#0(Jump:#1) // BinaryBranch(Simple,Exit) | Basic#1(Jump:#2,#3) { // Simple(BinaryBranch) | Basic#2(Jump:#1) } } // Exit | Basic#3(Ret) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1", }), b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: new[] { "2", "3" }), b => ValidateJmpBlock(b, expectedId: "2", expectedJumps: new[] { "1" }), b => ValidateRetBlock(b, expectedId: "3", expectedReturnExpression: ConstValue) ); }
public void SpecialBlocks_Using_Statement() { const string code = @" public class Class1 { public void Foo(Func<IDisposable> factory) { using (var x = factory()) // Jump(Next:UsingEnd) | Basic#0(Jump:#1) { // UsingEnd(Next:Exit) | Basic#1(Jump:#2) } // Exit | Basic#2(Ret:Const) } }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1" }), b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: new[] { "2" }), b => ValidateRetBlock(b, expectedId: "2", expectedReturnExpression: ConstValue) ); }
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(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}""] } "); }
public void Throw_Exception() { const string code = @" public class Class1 { public string Foo(string s) { if (true) // Branch(Jump,Exit) | Basic#0(Jump:#1,#2) { throw new Exception(); // Jump(Exit) | Basic#1(Jump:#3) } return s; // Jump(Exit) | Basic#2(Ret:s) } // Exit | Basic#3(Ret:Const) }"; var ucfg = UcfgVerifier.GetUcfgForMethod(code, "Foo"); ucfg.Entries.Should().BeEquivalentTo(new[] { "0" }); TestHelper.AssertCollection(ucfg.BasicBlocks, b => ValidateJmpBlock(b, expectedId: "0", expectedJumps: new[] { "1", "2" }), b => ValidateJmpBlock(b, expectedId: "1", expectedJumps: "3"), b => ValidateRetBlock(b, expectedId: "2", expectedReturnExpression: "s"), b => ValidateRetBlock(b, expectedId: "3", expectedReturnExpression: ConstValue)); }