This page supplies additional material for the extended abstract “*Assessing the Coverage of Formal Specifications*“, submitted to the PhD symposium “Algorithms, Tools and Applications” at iFM 2017.

In the extended abstract, we discussed a simple “find” method trying to discover an element in an integer array and returning its index, or -1 if the element was not present. In the section below, you find the source code of the method, along with the strongest JML specification that we discovered using our tool. Afterward, we present several specifications and the results produced for them by the tool. Finally, we provide an executable of the prototype with some usage instructions.

**Update** (2017/10/09): There is a new version of the tool which deviates in its output from the one discussed in the extended abstract. You find it here. The category of “use case facts” has been removed, the output is more verbose, and it features the possibility of interaction with the generated proof (e.g., to manually recover from prover incapacities). Also, the GUI has been improved.

## Source code and Strongest Specification for “find”

https://gist.github.com/rindPHI/c0bf00c371e9491a7598840ec5063f91## Different Specifications for “find”

The following table shows the different specification versions for “find” which we studied, ordered by increasing strenght / coverage.

Name | Post Condition | Loop Invariant | Ghost variable | Ghost field |
---|---|---|---|---|

SimplePost | \result == -1 || arr[\result] == n |
true |
X | X |

StrongerInv | \result == -1 || arr[\result] == n |
result == -1 || arr[result] == n |
X | X |

StrongerInvCaseDist | \result == -1 || arr[\result] == n |
i >= 0 && i <= arr.length && ( result != -1 || (\forall int k; k >= 0 && k < i; arr[k] != n)) && ( result == -1 || arr[result] == n) |
X | X |

StrongerInvConcreteResult | \result == -1 || arr[\result] == n |
i >= 0 && i <= arr.length && ( result != -1 || (\forall int k; k >= 0 && k < i; arr[k] != n)) && ( result == -1 || arr[result] == n && result == i-1) |
X | X |

StrongerPost | ((\exists int i; i >= 0 && i < arr.length; arr[i] == n) ==> arr[\result] == n) && ((\forall int i; i >= 0 && i < arr.length; arr[i] != n) ==> \result == -1) |
i >= 0 && i <= arr.length && ( result != -1 || (\forall int k; k >= 0 && k < i; arr[k] != n)) && ( result == -1 || arr[result] == n && result == i-1) |
X | X |

InvWithLastRun | ((\exists int i; i >= 0 && i < arr.length; arr[i] == n) ==> arr[\result] == n) && ((\forall int i; i >= 0 && i < arr.length; arr[i] != n) ==> \result == -1) |
i >= 0 && i <= arr.length && i = iLastRun + 1 && ( result != -1 || (\forall int k; k >= 0 && k < i; arr[k] != n)) && ( result == -1 || arr[result] == n && result == i-1) |
ghost int iLastRun = i - 1; |
X |

PostWithLastRun | ((\exists int i; i >= 0 && i < arr.length; arr[i] == n) ==> arr[\result] == n && \result == f_iLastRun) && ((\forall int i; i >= 0 && i < arr.length; arr[i] != n) ==> \result == -1) |
i >= 0 && i <= arr.length && i = f_iLastRun + 1 && ( result != -1 || (\forall int k; k >= 0 && k < i; arr[k] != n)) && ( result == -1 || arr[result] == n && result == i-1) |
X | ghost int f_iLastRun = i - 1; |

## Coverage Results for “find” Specifications

The following table depicts the results produced by our implementation for the specification versions outlined above. “**Strength 1**” is a percentage coverage of *covered* and *abstractly covered* post condition, loop body and use case facts. “**Strength 2**” *only* considers *covered* post condition and loop body facts (so, *no* use case facts). “**Strength 3**” is an extension of “Strength 2” which also considers abstractly covered post condition and loop body facts. The values for “Strength 1” and “Strength 3” are *only monotonic from the third line on*, since in “SimplePost” and “StrongerInv”, there are *exception / use case facts uncovered* which makes it easier to abstractly cover facts (for instance, “true” abstractly covers all possible facts, but does not exclude any exception and usually does not imply any non-trivial method post condition).

Post Condition Facts | Loop Body Facts | ||||||||
---|---|---|---|---|---|---|---|---|---|

Name | # Uncovered Exception Facts | # Uncovered Use Case Facts | Abstractly | Uncovered | Abstractly | Uncovered | Strength 1 | Strength 2 | Strength 3 |

SimplePost | 1 |
1 |
1 | 1 | 1 | 1 | 30.00% | 0.00% | 37.50% |

StrongerInv | 1 |
0 | 2 | 0 | 4 | 0 | 57.14% | 0.00% | 50.00% |

StrongerInvCaseDist | 0 | 0 | 2 | 0 | 0 | 4 | 42.86% | 0.00% | 33.33% |

StrongerInvConcreteResult | 0 | 0 | 2 | 0 | 0 | 2 | 57.14% | 33.33% | 50.00% |

StrongerPost | 0 | 0 | 0 | 0 | 0 | 2 | 72.22% | 50.00% | 58.33% |

InvWithLastRun | 0 | 0 | 0 | 1 | 0 | 0 | 95.45% | 87.50% | 93.75% |

PostWithLastRun | 0 | 0 | 0 | 0 | 0 | 0 | 100% | 100% | 100% |

The chart below illustrates the progession of the strength measures.

## Tool Download and Usage Instructions

Download the executable of the prototype

After downloading the above ZIP file, just unzip it anywhere on your drive. A Java Runtime Environment of version 8 or higher is required to start the KeY version linked on this page.

To execute the strength analysis CLI, run `java -cp KeY.jar de.tud.cs.se.ds.specstr.cli.Main`

, which will display the following help:

=========================================== This is SpecStr v0.1 a tool for assessing the strength of formal specifications using symbolic execution =========================================== usage: java -cp KeY.jar de.tud.cs.se.ds.specstr.cli.Main [input Java file] [fully qualified method name] -h,--help Display help (this text) and terminate -o,--out-file <arg> Save output to this file -p,--proof-out-file <arg> Save proof to this file

For instance, starting it on the “find” method with the specification SimplePost (by executing `java -cp KeY.jar de.tud.cs.se.ds.specstr.cli.Main /path/to/Find.java "Find::find([II)I"`

), we obtain as output

===================== Unhandled Exceptions: ===================== Unclosed exception node "Index Out of Bounds (_arr_0 != null, but i Out of Bounds!)", path condition: "result_1_0 = -1 & ( arr_0.length > i_0 & (arr_0.length <= i_0 | i_0 < 0))" ================ Uncovered Facts: ================ Post condition implies final state fact, Path condition "!result_1_0 = -1" result = result_1_0 Loop use case fact, Path condition "!result_1_0 = -1" arr_0[result_1_0]@heap[anon({}, anon_heap_LOOP)] = n ========================= Abstractly Covered Facts: ========================= Post condition implies final state fact, Path condition "result_1_0 = -1 & arr_0.length <= i_0" result = -1 Loop body fact, Path condition "result_1_0 = -1 & (arr_0.length > i_0 & !arr_0[i_0] = n)" i = 1 + i_0 Loop body fact, Path condition "result_1_0 = -1 & (arr_0.length > i_0 & arr_0[i_0] = n)" i = 1 + i_0 Covered 3 (0 concretely, 3 abstractly) out of 5 facts Overall Strength: 30,00% Concrete Effects Strength: 0,00% Abstract Effects Strength: 37,50%

The output can be written to a text file using the “-o” flag. With the “-p” flag, you can export the KeY proof tree (which is basically an extension of the symbolic execution tree) on which our analysis works. Then, you can start KeY by just running `java -jar KeY.jar`

and loading the “.proof” file, and inspect the details of the analysis. Basically, everything is handled by dedicated proof rules which are prefixed by “Analyze…”, so you can search in the tree by pressing ` ctrl + shift + f`, entering “Analyze” and jumping from match to match.