OpenJML入门
OpenJML 获取
下载
OpenJML
下载可以通过其github
仓库获取。传送门
使用
下载完成后,可以直接使用命令行进行操作,但是比较麻烦。这里提供两个版本的辅助文件简化操作
Linux
# filename : openjml
#!/bin/bash
java -jar path/to/openjml.jar "$@"
其中jar
包的路径需要自行修改。
Windows
::openjml.bat
@echo off
set java=java -jar "%~dp0openjml.jar" -noPurityCheck
set prove=-prover cvc4 -exec "%~dp0Solvers-windows\cvc4-1.6.exe"
set runtime=-cp %~dp0jmlruntime.jar;
set allparam=
set rac=
:param
set str=%1
if "%str%"=="" (
goto end
)
if "%str%"=="-rac" (
set rac=rac
)
if "%str%"=="-prove" (
set str=%prove%
)
set allparam=%allparam% %str%
shift /0
goto param
:end
if "%allparam%"=="" (
goto eof
)
rem remove left right blank
:intercept_left
if "%allparam:~0,1%"==" " set "allparam=%allparam:~1%"&goto intercept_left
:intercept_right
if "%allparam:~-1%"==" " set "allparam=%allparam:~0,-1%"&goto intercept_right
:eof
%java% %allparam%
set filename=
if "%rac%"=="rac" (
:input
set /p filename=Input file name:
if "%filename%"=="" (
goto input
)
java %runtime% %filename%
)
pause
用户可以将上述代码拷贝到解压后的文件夹中,然后将该文件夹加入环境变量。即可通过命令行调用openjml
命令了。这里为了简化验证时的参数传递,提供了一个-prove
参数。用户指定这一参数后,程序将选定prover
并执行。
Parsing and Type-checking
OpenJML
最基本的功能就是对JML
注释的完整性进行检查。检查包括经典的类型检查、变量可见性与可写性等。通过命令行使用OpenJML
时,可以通过-check
参数(缺省)指定类型检查。
openjml [-check] options files
举例来说
public class main {
/*@
@ require args != null
@*/
public static void main(String[] args) {
System.out.println(args);
}
}
运行语法检查后报错
C:\>openjml main.java
main.java:17: 错误: 需要';'
@ require args != null
^
main.java:17: 错误: 找不到符号
@ require args != null
^
符号: 类 require
位置: 类 main
2 个错误
类型检查检测出了注释中的两处错误。分别更改后的程序如下
public class main {
/*@
@ requires args != null;
@*/
public static void main(String[] args) {
System.out.println(args);
}
}
再次运行后即可通过检查。
Extended Static Checking
类型检查只能确保jml
注释的格式正确,但是对规格内容不进行检查。下述程序中明显违背了规格的描述,但是类型检查仍然通过了。
public class main {
/*@
@ requires a != 0 && b != 0;
@ ensures \result <= 1 && \result >= 0;
@*/
public static int fun(int a, int b) {
return a > b ? 1 : 0;
}
public static void main(String[] args) {
fun(1, 0);
}
}
为了对规格内容进行检查,需要使用-esc
参数
C:\>openjml -esc -prove main.java
main.java:10: 警告: The prover cannot establish an assertion (Precondition: main.java:6: 注: ) in method main
fun(1, 0);
^
main.java:6: 警告: Associated declaration: main.java:10: 注:
public static int fun(int a, int b) {
^
main.java:3: 警告: Precondition conjunct is false: b != 0
@ requires a != 0 && b != 0;
^
3 个警告
这里需要注意,静态检查需要指定prover
。在上述Windows
版的批处理函数中,默认指定了一个prover
。用户也可以使用-prover
等参数指定自定义检查器。
那么esc
是如何完成的呢?好奇的话可以通过-progress
参数或-verboseness
参数来查看检查的具体过程。下面截取了部分输出
C:\>openjml -esc -prove main.java -verboseness=3
...
STARTING PROOF OF main.main(java.lang.String[])
/*@
public behavior
requires args != null;
signals_only java.lang.RuntimeException;
assignable \everything;
accessible \everything;
*/
{
fun(1, 0);
}
...
可以看出OpenJML
在验证前会首先重整规格,而后针对性生成测试程序。由于生成程序过长,可以在本地尝试。此处使用的是-verboseness=3
,如果使用 4 将会输出大量信息。
Runtime Assertion Checking
使用-rac
选项可以执行运行时检查。上述批处理文件中存在 bug,第一次输入文件名时可能会要求再次输入。
C:\>openjml -rac main.java
Input file name:main
main.java:10: 警告: JML precondition is false
fun(1, 0);
^
main.java:6: 警告: Associated declaration: main.java:10: 注:
public static int fun(int a, int b) {
^
这时会自动运行生成出来的.class
文件,但经过反汇编后我的.class
文件并不能编译。因此目前还没有看懂rac
的具体流程。此外,-rac
选项有许多补充选项,功能十分丰富,大家可以多多尝试。
参考
OpenJML 基本使用 伦泽标