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 基本使用 伦泽标

posted @ 2019-05-05 14:41  LutingWang  阅读(1881)  评论(4编辑  收藏  举报