Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2045 cfg implement abs int driver #2050

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions TypeCobol.Analysis.Test/BasicAbstractInterpretationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TypeCobol.Compiler.CodeModel;
using TypeCobol.Compiler.Nodes;
using System.IO;
using System.Runtime.CompilerServices;
using TypeCobol.Analysis.Graph;

using static TypeCobol.Analysis.Test.CfgTestUtils;

namespace TypeCobol.Analysis.Test
{
/// <summary>
/// These are basic tests for control from instructions.
/// </summary>
[TestClass]
public class BasicAbstractInterpretationTest
{
/// <summary>
/// This is a template method for a simple test on a source file
/// containing a single program.
/// Performs parsing, diagnostics comparison and CFG comparison.
/// </summary>
/// <remarks>
/// All parameters are optional, by default :
/// - input is BasicCfgInstrs\[CallerMethod].cbl
/// - expected result is BasicCfgInstrs\[CallerMethod].int
/// - CFG building mode is Standard
/// - fullInstruction is True
/// </remarks>
/// <param name="inputDirectoryPath">Full path to the directory containing the input source file.</param>
/// <param name="inputFileName">Name of the input source file without extension.</param>
/// <param name="inputExtension">Extension of the input source file including the '.'.</param>
/// <param name="expectedResultDirectoryPath">Full path to the directory containing the expected result file.</param>
/// <param name="expectedResultFileName">Name of the expected result file without extension.</param>
/// <param name="expectedResultExtension">Extension of the expected result file including the '.'.</param>
/// <param name="mode">CFG Building mode.</param>
/// <param name="fullInstruction">True to use full instruction during dot generation.</param>
private void TestTemplate(
string inputDirectoryPath = null,
[CallerMemberName] string inputFileName = null,
string inputExtension = ".cbl",
string expectedResultDirectoryPath = null,
[CallerMemberName] string expectedResultFileName = null,
string expectedResultExtension = ".int",
CfgBuildingMode mode = CfgBuildingMode.Standard,
bool fullInstruction = true)
{
string inputFilePath = Path.Combine(inputDirectoryPath ?? BasicCfgInstrs, inputFileName + inputExtension);
Assert.IsTrue(File.Exists(inputFilePath), $"Input file '{inputFilePath}' does not exist.");

string expectedResultFilePath = Path.Combine(expectedResultDirectoryPath ?? BasicCfgInstrs, expectedResultFileName + expectedResultExtension);
Assert.IsTrue(File.Exists(expectedResultFilePath), $"Expected results file '{expectedResultFilePath}' does not exist.");

var graphs = ParseCompareDiagnostics<object>(inputFilePath, mode);
Assert.IsTrue(graphs.Count == 1); //single program
var cfg = graphs[0];
Assert.IsNull(cfg.ParentGraph);
Assert.IsTrue(cfg.ProgramOrFunctionNode is Program);
Assert.IsNull(cfg.NestedGraphs);
GenAbstractInterpretCfgAndCompare(cfg, 1, inputFilePath, expectedResultFilePath, fullInstruction);
}

[TestMethod]
public void IfThen0() => TestTemplate();

[TestMethod]
public void IfThenElse0() => TestTemplate();

[TestMethod]
public void IfThenNested0() => TestTemplate();

[TestMethod]
public void IfThenNested1() => TestTemplate();

[TestMethod]
public void IfThenNested2() => TestTemplate();

[TestMethod]
public void IfThenElseCascade0() => TestTemplate();

[TestMethod]
public void IfThenElseNested1() => TestTemplate();

[TestMethod]
public void IfThenNextSentence0() => TestTemplate();

[TestMethod]
public void IfThenElseNextSentence0() => TestTemplate();

[TestMethod]
public void IfThenElseNextSentence1() => TestTemplate();

[TestMethod]
public void IfThenElseNextSentence2() => TestTemplate();

[TestMethod]
public void Evaluate0() => TestTemplate();

[TestMethod]
public void EvaluateMultiWhen0() => TestTemplate();

[TestMethod]
public void EvaluateNoOther0() => TestTemplate();
[TestMethod]
public void MixPerformEvaluateIf0() => TestTemplate();
[TestMethod]
public void ComplexGotoPara0() => TestTemplate();

[TestMethod]
public void ComplexCyclicGotoPara0() => TestTemplate();

[TestMethod]
public void Declaratives0() => TestTemplate();

[TestMethod]
public void Declaratives1() => TestTemplate();

}
}
35 changes: 35 additions & 0 deletions TypeCobol.Analysis.Test/BasicCfgInstrs/ComplexCyclicGotoPara0.cbl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
 IDENTIFICATION DIVISION.
PROGRAM-ID. GOTOCplx.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 m PIC 9 VALUE 2.

PROCEDURE DIVISION.
PARA-A.
DISPLAY 'IN PARA-A'
GO TO PARA-C.

PARA-B.
DISPLAY 'IN PARA-B '.

PARA-C.
DISPLAY 'IN PARA-C '.
GO TO PARA-E PARA-F PARA-G DEPENDING ON m.

PARA-D.
DISPLAY 'IN PARA-D '.

PARA-E.
DISPLAY 'IN PARA-E '.
GO TO PARA-C.

PARA-F.
DISPLAY 'IN PARA-F '.

PARA-G.
DISPLAY 'IN PARA-G '.

GOBACK.
END PROGRAM GOTOCplx.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--- Diagnostics ---
Line 14[8,27] <37, Warning, General> - Warning: Unreachable code detected
56 changes: 56 additions & 0 deletions TypeCobol.Analysis.Test/BasicCfgInstrs/ComplexCyclicGotoPara0.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
digraph Cfg {
node [
shape = "record"
]

edge [
arrowtail = "empty"
]
Block0 [
label = "{START|}"
]
Block1 [
label = "{PARA-A. Block1|DISPLAY 'IN PARA-A'\lGO TO PARA-C\l}"
]
Block4 [
label = "{PARA-C. Block4|DISPLAY 'IN PARA-C '\l}"
]
Block5 [
label = "{PARA-C. Block5|GO TO PARA-E PARA-F PARA-G DEPENDING ON m\l}"
]
Block6 [
label = "{Block6|}"
]
Block7 [
label = "{PARA-D. Block7|DISPLAY 'IN PARA-D '\l}"
]
Block8 [
label = "{PARA-E. Block8|DISPLAY 'IN PARA-E '\l}"
]
Block9 [
label = "{PARA-E. Block9|GO TO PARA-C\l}"
]
Block11 [
label = "{PARA-F. Block11|DISPLAY 'IN PARA-F '\l}"
]
Block12 [
label = "{PARA-G. Block12|DISPLAY 'IN PARA-G '\l}"
]
Block13 [
label = "{PARA-G. Block13|GOBACK\l}"
]
Block0 -> Block1
Block1 -> Block4
Block4 -> Block5
Block5 -> Block6
Block5 -> Block8
Block5 -> Block11
Block5 -> Block12
Block6 -> Block7
Block7 -> Block8
Block8 -> Block9
Block9 -> Block4
Block11 -> Block12
Block12 -> Block13

}
87 changes: 87 additions & 0 deletions TypeCobol.Analysis.Test/BasicCfgInstrs/ComplexCyclicGotoPara0.int
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Entering block : 0
Leaving block : 0
Entering block : 1
DISPLAY 'IN PARA-A'
GO TO PARA-C
Leaving block : 1
Entering block : 4 with Cyclic Execution Threshold = 1
DISPLAY 'IN PARA-C '
Leaving block : 4 with Cyclic Execution Threshold = 0
Entering block : 5
GO TO PARA-E PARA-F PARA-G DEPENDING ON m
Leaving block : 5
Entering block : 12
DISPLAY 'IN PARA-G '
Leaving block : 12
Entering block : 13
GOBACK
Leaving block : 13
Entering block : 11
DISPLAY 'IN PARA-F '
Leaving block : 11
Entering block : 12
DISPLAY 'IN PARA-G '
Leaving block : 12
Entering block : 13
GOBACK
Leaving block : 13
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
GO TO PARA-C
Leaving block : 9
Entering block : 4 with Cyclic Execution Threshold = 0
DISPLAY 'IN PARA-C '
Leaving block : 4 with Cyclic Execution Threshold = -1
Entering block : 5
GO TO PARA-E PARA-F PARA-G DEPENDING ON m
Leaving block : 5
Entering block : 12
DISPLAY 'IN PARA-G '
Leaving block : 12
Entering block : 13
GOBACK
Leaving block : 13
Entering block : 11
DISPLAY 'IN PARA-F '
Leaving block : 11
Entering block : 12
DISPLAY 'IN PARA-G '
Leaving block : 12
Entering block : 13
GOBACK
Leaving block : 13
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
GO TO PARA-C
Leaving block : 9
Entering block : 6
Leaving block : 6
Entering block : 7
DISPLAY 'IN PARA-D '
Leaving block : 7
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
GO TO PARA-C
Leaving block : 9
Entering block : 6
Leaving block : 6
Entering block : 7
DISPLAY 'IN PARA-D '
Leaving block : 7
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
GO TO PARA-C
Leaving block : 9

=======
METRICS
=======
{EdgeCount=13; NodeCount=11; ControlSubgraphCount=0; HighCyclomaticComplexity=4; HighEssentialComplexityPath=4}
61 changes: 61 additions & 0 deletions TypeCobol.Analysis.Test/BasicCfgInstrs/ComplexGotoPara0.int
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Entering block : 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This program gives the following result when run:

IN PARA-A
IN PARA-C
IN PARA-F
IN PARA-G

So is the result of the file the reflect of a running program or something else ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A CFG reflect all possible executions of a program. But in reality this can represent exponential possibilities, so Abstract interpretation can only be view of a subset of all possibilities.
But the most important thing here we are just implementing a driver, we don't evaluate variable value to decide for instance what to do in the instruction GO TO PARA-E PARA-F PARA-G DEPENDING ON m
depending on the value of m.
We are not intrepreting the COBOL program.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on the default execution performed is added. 7f8af6b
Not all possible execution paths can be followed, in this default implementation. But it is the base for other execution scheme.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this code:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MyPGM.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  Group1.
           05 X pic X occurs 100 indexed by Idx.
           05 Y pic X occurs  50 indexed by Idx2.
       PROCEDURE DIVISION.
           MOVE 0   TO IDX2

           PERFORM VARYING Idx FROM 1 BY 1
                             UNTIL Idx > 100
      
               IF (IDX2 < 50)
                              PERFORM ADD-ITEM
               END-IF
           END-PERFORM
      *    Ko, ADD-ITEM increment Idx2 without enclosing IF
           PERFORM ADD-ITEM
           goback
           .
       ADD-ITEM.
           display "ADD-ITEM"
           ADD 1  TO Idx2
           MOVE X(Idx)   TO Y(IDX2)
           perform DisplayELt
           .
       DisplayELt.
           display "X(Idx)=" X(Idx)  
           .
      
       END PROGRAM MyPGM.
      

Paragraph ADD-ITEM is called outside of an IF statement (just before the goback), and our custom quality rule must detect it.

Does this PR handle this case?
On my execution I get:

    MOVE 0   TO IDX2
Entering block : 1
    PERFORM VARYING Idx FROM 1 BY 1
                      UNTIL Idx > 100
Leaving block : 1
Entering block : 2
        IF (IDX2 < 50)
Entering block : 3
Leaving block : 3
Entering block : 4
                       PERFORM ADD-ITEM
Leaving block : 4
Entering block : 5
Leaving block : 5
Leaving block : 2
Entering block : 6
Leaving block : 6
Leaving block : 0
Entering block : 7
Leaving block : 7
Entering block : 8
    PERFORM ADD-ITEM
Leaving block : 8
Entering block : 9
    goback
Leaving block : 9

which is not clear for me if the paragraph ADD-ITEM if the driver will pass on it 2 times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this the result:
--- Diagnostics ---
Line 9[12,27] <47, Warning, CodeAnalysis> - [Qualis] Protect table index increment (Incremented table index indX was not conditionnaly protected by IF or PERFORM UNTIL clause.)
Line 24[12,25] <47, Warning, CodeAnalysis> - [Qualis] Protect table index increment (Incremented table index indX was not conditionnaly protected by IF or PERFORM UNTIL clause.)

That is to say:
MOVE 0 TO IDX2
and
ADD 1 TO Idx2

Are detected.

Leaving block : 0
Entering block : 1
DISPLAY 'IN PARA-A'
GO TO PARA-C
Leaving block : 1
Entering block : 4
DISPLAY 'IN PARA-C '
Leaving block : 4
Entering block : 5
GO TO PARA-E PARA-F PARA-G DEPENDING ON m
Leaving block : 5
Entering block : 10
DISPLAY 'IN PARA-G '
Leaving block : 10
Entering block : 11
GOBACK
Leaving block : 11
Entering block : 9
DISPLAY 'IN PARA-F '
Leaving block : 9
Entering block : 10
DISPLAY 'IN PARA-G '
Leaving block : 10
Entering block : 11
GOBACK
Leaving block : 11
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
DISPLAY 'IN PARA-F '
Leaving block : 9
Entering block : 10
DISPLAY 'IN PARA-G '
Leaving block : 10
Entering block : 11
GOBACK
Leaving block : 11
Entering block : 6
Leaving block : 6
Entering block : 7
DISPLAY 'IN PARA-D '
Leaving block : 7
Entering block : 8
DISPLAY 'IN PARA-E '
Leaving block : 8
Entering block : 9
DISPLAY 'IN PARA-F '
Leaving block : 9
Entering block : 10
DISPLAY 'IN PARA-G '
Leaving block : 10
Entering block : 11
GOBACK
Leaving block : 11

=======
METRICS
=======
{EdgeCount=12; NodeCount=10; ControlSubgraphCount=0; HighCyclomaticComplexity=4; HighEssentialComplexityPath=4}
21 changes: 21 additions & 0 deletions TypeCobol.Analysis.Test/BasicCfgInstrs/Declaratives0.int
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Entering block : 0
Leaving block : 0
Entering block : 4
Leaving block : 4
Entering block : 5
DISPLAY "TOTO"
Leaving block : 5
Entering block : 1
USE FOR DEBUGGING ON ALL PROCEDURES.
Leaving block : 1
Entering block : 2
display DEBUG-NAME
Leaving block : 2
Entering block : 3
EXIT
Leaving block : 3

=======
METRICS
=======
{EdgeCount=5; NodeCount=6; ControlSubgraphCount=0; HighCyclomaticComplexity=1; HighEssentialComplexityPath=1}
Loading