JML语言概述(Level 0)

概念定义

JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言。JML是一种行为接口规格语言(Behavior Interface Specification Language,BISL),基于Larch方法构建。

  由上述定义可知,JML诞生的初衷就是对代码进行形式化描述,试图在用自然语言表达的需求与用代码语言表达的实现之间构建一座桥梁,一方面可以消除自然语言可能存在的歧义与模糊,另一方面也可以屏蔽具体的代码实现,实现另一种形式的抽象

JML基本语法

表达式 含义
\result 方法的返回值(非void类型)
\old(expr) 表达式expr在方法执行前的取值
\not_assigned(x, y, ...) 括号中的变量是否在方法执行过程中未被赋值
\not_modified(x, y, ...) 限制括号中的变量在方法执行期间的取值未发生变化
(\forall T x; R(x); P(x)) 全称量词
(\exists T x; R(x); P(x)) 存在量词
(\sum T x; R(x); expr) 返回给定范围内的表达式的和
(\max T x; R(x); expr) 返回给定范围内的表达式的最大值
(\min T x; R(x); expr) 返回给定范围内的表达式的最小值
<==> 等价操作符
==> 蕴含操作符
\nothing 空集

 

方法规格 含义
requires 前置条件,表达的意思是“要求调用者确保P为真”。
ensures 后置条件,表达的意思是“方法实现者确保方法执行返回结果一定满足谓词P的要求,即确保P为真”。
assignable 副作用指方法在执行过程中会修改对象的属性数据或者类的静态成员数据,从而给后续方法的执行带来影响。
public normal_behavior 正常功能,一般指输入或方法关联this对象的状态在正常范围内时所指向的功能。
public exceptional_behavior 异常功能,与正常功能相对
signals 结构为signals (***Exception e) b_expr;表示抛出异常

 

类型规格 含义
invariant 不变式(invariant)是要求在所有可见状态下都必须满足的特性
constraint 对前序可见状态和当前可见状态的关系进行约束

JML工具链

  通过上文,我们已经看到,JML在形式化描述上就语言规范来说已经非常完善了,可以说是把谓词逻辑的那一套完完整整地搬了过来,很好地保证了严谨性,也相对代码要更加易读一些。

  但是!!!这里面有两个问题。

  首先,JML并不对方法的复杂度有任何限制。于是我们可以看到,在某些方法里,比如addRelation(),方法本身复杂度并不高,但JML却特别长,让人看得很不耐烦,还不如直接看代码来得简洁明了;而对另一些场景,比如返回的对象比较复杂不是一个简单基本类型的话,就会出现关键字嵌套的情况,这就使得单条JML语句的复杂度也会很高,还不如拆成多行的代码看的清楚。特别地,我尤其对于如何用JML来写递归感到好奇,毕竟我依稀记得一阶逻辑似乎是不完备的哦?

  所以这时候可能就有人会说,JML不是写给程序员看的,程序员看代码就好了。那么问题又来了,JML的理想服务对象是谁呢?是掌握谓词逻辑的产品经理?不过,如果真是这样,那我也可以接受。但似乎,JML工具链却为我们指出了另一个潜在服务对象:机器

  简单来说,JML工具链似乎就是为了能够让机器基于规格以形式化验证的方式来代替人类对方法进行完备的测试而诞生的。怎么说呢,光是看这句话就挺心疼人家机器的——这要求也忒高了吧。别说AI了,就是人,你能保证他做到绝对完备吗?他能考虑到一个顾客进来问现在几点了这种操作吗?

  为什么说让机器来代替人类进行测试是不现实的呢?很简单,因为机器也是人造的,要想实现基于形式化语言可泛化测试,而不基于具体的需求本身,就意味着你必须要仅仅凭借上面的那些关键字,就能枚举出它们所有潜在可能组合,而且这个组合中的每一种情况所包含的测试样例还必须是有穷,有阶梯性的。你觉得,如果我随手写一行一阶谓词逻辑,你有信心告诉我这玩意的所有边界情况吗?更何况,你知道的只是一个谓词集合呢?

  或许,可能开发者的初衷是只要我把每个前置条件与后置条件还有副作用写清楚,机器针对不同的behavior生成不同的测试样例这样。但是,需要特别指出的是,同一个requires语句下的不同的边界数据才是构造测试的关键,特别是面对非欧拓扑模型(很不巧,我们的Unit3就长这样)。

  所以啊,别难为人家开发者和机器了。指望靠JML工具实现自动测试还是等到强AI出来并且还没来得及毁灭人类的时候吧。

  吐槽到此结束,对于JML工具链的具体介绍,请参见这个传送门。下文仅涉及openjml与JMLUnitNG的极少数方法。

OpenJML形式化验证

  openjml是目前相对较为完备的一套形式化验证Kit,但是遗憾的是它在jdk1.8之后就停止更新了,于是对于我这个一开始用jdk12.0的人就很不友好(上来就把人劝退的那种)。

  下面我们以task2中的实例化类RealNetwork为例,演示该工具的使用与其相应结果。假设IDEA项目所在目录为$PATH。

Parsing and Type checking

  运行命令:

java -jar openjml.jar -check $PATH\src\network\RealNetwork.java -sourcepath $PATH\src -encoding utf-8

  程序正常返回,无任何报错信息。

Extended Static Checking

  运行命令:

java -jar openjml.jar -prover z3_4_7 -exec .\Solvers-windows\z3-4.7.1.exe -esc $PATH\src\network\RealNetwork.java -sourcepath $PATH\src -encoding utf-8

  其中,-prover参数用于指定Solver类型,-exec参数用于指定Solver可执行程序,-esc参数指定检查类型为Extended Static Checking。

  返回结果包含3个错误信息和100条警告,经过简单归类后,大致为以下几类:

注: Not implemented for static checking: ensures clause containing \not_assigned

错误: An internal JML error occurred, possibly recoverable. Please report the bug with as much information as you can. Reason: Internal exception: class java.lang.NullPointerException

警告: The prover cannot establish an assertion (ExceptionalPostcondition:

警告: Associated declaration:

警告: The prover cannot establish an assertion (UndefinedCalledMethodPrecondition:

警告: Precondition conjunct is false:

错误: ESC could not be attempted because of a failure in typechecking or AST transformation: queryNameRank

至于为什么只找到2个错误,我也不知道

  总之,给人一种不明觉厉的感觉吧。(输出信息有近450行,我就不放了)

Runtime Assertion Checking

  运行命令:

java -jar openjml.jar -rac $PATH\src\network\RealNetwork.java -sourcepath $PATH\src -encoding utf-8

  其中,-rac参数指定检查类型为Runtime Assertion Checking。

  运行结果如下:

 

  1 The operation symbol ++ for type java.lang.Object could not be resolved
  2 org.jmlspecs.openjml.JmlInternalError: The operation symbol ++ for type java.lang.Object could not be resolved
  3         at org.jmlspecs.openjml.JmlTreeUtils.findOpSymbol(JmlTreeUtils.java:291)
  4         at org.jmlspecs.openjml.JmlTreeUtils.findOpSymbol(JmlTreeUtils.java:282)
  5         at org.jmlspecs.openjml.JmlTreeUtils.makeUnary(JmlTreeUtils.java:739)
  6         at com.sun.tools.javac.comp.JmlAttr.createRacExpr(JmlAttr.java:4465)
  7         at org.jmlspecs.openjml.ext.QuantifiedExpressions$QuantifiedExpression.typecheck(QuantifiedExpressions.java:214)
  8         at com.sun.tools.javac.comp.JmlAttr.visitJmlQuantifiedExpr(JmlAttr.java:4070)
  9         at org.jmlspecs.openjml.JmlTree$JmlQuantifiedExpr.accept(JmlTree.java:2685)
 10         at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:577)
 11         at com.sun.tools.javac.comp.Attr.visitParens(Attr.java:2995)
 12         at com.sun.tools.javac.tree.JCTree$JCParens.accept(JCTree.java:1661)
 13         at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:577)
 14         at com.sun.tools.javac.comp.Attr.attribExpr(Attr.java:619)
 15         at com.sun.tools.javac.comp.JmlAttr.attribExpr(JmlAttr.java:6209)
 16         at com.sun.tools.javac.comp.JmlAttr.visitJmlMethodClauseExpr(JmlAttr.java:3117)
 17         at org.jmlspecs.openjml.JmlTree$JmlMethodClauseExpr.accept(JmlTree.java:2332)
 18         at com.sun.tools.javac.comp.JmlAttr.visitJmlSpecificationCase(JmlAttr.java:3361)
 19         at org.jmlspecs.openjml.JmlTree$JmlSpecificationCase.accept(JmlTree.java:2837)
 20         at com.sun.tools.javac.comp.JmlAttr.visitJmlMethodSpecs(JmlAttr.java:3423)
 21         at org.jmlspecs.openjml.JmlTree$JmlMethodSpecs.accept(JmlTree.java:2539)
 22         at com.sun.tools.javac.comp.JmlAttr.checkMethodSpecsDirectly(JmlAttr.java:1560)
 23         at com.sun.tools.javac.comp.JmlAttr.visitMethodDef(JmlAttr.java:1121)
 24         at com.sun.tools.javac.comp.JmlAttr.visitJmlMethodDecl(JmlAttr.java:6053)
 25         at org.jmlspecs.openjml.JmlTree$JmlMethodDecl.accept(JmlTree.java:1261)
 26         at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:577)
 27         at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:646)
 28         at com.sun.tools.javac.comp.JmlAttr.attribStat(JmlAttr.java:558)
 29         at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:4378)
 30         at com.sun.tools.javac.comp.JmlAttr.attribClassBody(JmlAttr.java:536)
 31         at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4286)
 32         at com.sun.tools.javac.comp.JmlAttr.attribClass(JmlAttr.java:414)
 33         at com.sun.tools.javac.comp.JmlAttr.completeTodo(JmlAttr.java:492)
 34         at com.sun.tools.javac.comp.JmlAttr.attribClass(JmlAttr.java:458)
 35         at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4215)
 36         at com.sun.tools.javac.comp.Attr.attrib(Attr.java:4190)
 37         at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1258)
 38         at com.sun.tools.javac.main.JmlCompiler.attribute(JmlCompiler.java:479)
 39         at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:898)
 40         at com.sun.tools.javac.main.JmlCompiler.compile2(JmlCompiler.java:712)
 41         at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:867)
 42         at com.sun.tools.javac.main.Main.compile(Main.java:553)
 43         at com.sun.tools.javac.main.Main.compile(Main.java:410)
 44         at org.jmlspecs.openjml.Main.compile(Main.java:581)
 45         at com.sun.tools.javac.main.Main.compile(Main.java:399)
 46         at com.sun.tools.javac.main.Main.compile(Main.java:390)
 47         at org.jmlspecs.openjml.Main.execute(Main.java:417)
 48         at org.jmlspecs.openjml.Main.execute(Main.java:375)
 49         at org.jmlspecs.openjml.Main.execute(Main.java:362)
 50         at org.jmlspecs.openjml.Main.main(Main.java:334)
 51 错误: An internal JML error occurred, possibly recoverable.  Please report the bug with as much information as you can.
 52   Reason: com.sun.tools.javac.code.Type$BottomType cannot be cast to com.sun.tools.javac.code.Type$ArrayType
 53   java.lang.ClassCastException: com.sun.tools.javac.code.Type$BottomType cannot be cast to com.sun.tools.javac.code.Type$ArrayType
 54         at org.jmlspecs.openjml.JmlTreeUtils.copyArray(JmlTreeUtils.java:1546)
 55         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitJmlMethodInvocation(JmlAssertionAdder.java:15456)
 56         at org.jmlspecs.openjml.JmlTree$JmlMethodInvocation.accept(JmlTree.java:2229)
 57         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 58         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 59         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 60         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitParens(JmlAssertionAdder.java:10686)
 61         at com.sun.tools.javac.tree.JCTree$JCParens.accept(JCTree.java:1661)
 62         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 63         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 64         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 65         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitBinary(JmlAssertionAdder.java:11862)
 66         at com.sun.tools.javac.tree.JCTree$JCBinary.accept(JCTree.java:1785)
 67         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 68         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 69         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 70         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertJML(JmlAssertionAdder.java:1647)
 71         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertJML(JmlAssertionAdder.java:1664)
 72         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertNoSplit(JmlAssertionAdder.java:1685)
 73         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitJmlQuantifiedExpr(JmlAssertionAdder.java:16233)
 74         at org.jmlspecs.openjml.JmlTree$JmlQuantifiedExpr.accept(JmlTree.java:2685)
 75         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 76         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 77         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 78         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitParens(JmlAssertionAdder.java:10686)
 79         at com.sun.tools.javac.tree.JCTree$JCParens.accept(JCTree.java:1661)
 80         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 81         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 82         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 83         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertJML(JmlAssertionAdder.java:1647)
 84         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertJML(JmlAssertionAdder.java:1664)
 85         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertNoSplit(JmlAssertionAdder.java:1685)
 86         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitJmlQuantifiedExpr(JmlAssertionAdder.java:16233)
 87         at org.jmlspecs.openjml.JmlTree$JmlQuantifiedExpr.accept(JmlTree.java:2685)
 88         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 89         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 90         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 91         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitParens(JmlAssertionAdder.java:10686)
 92         at com.sun.tools.javac.tree.JCTree$JCParens.accept(JCTree.java:1661)
 93         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
 94         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
 95         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertExpr(JmlAssertionAdder.java:1504)
 96         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertJML(JmlAssertionAdder.java:1647)
 97         at org.jmlspecs.openjml.esc.JmlAssertionAdder.addPostConditions(JmlAssertionAdder.java:4758)
 98         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convertMethodBodyNoInit(JmlAssertionAdder.java:1195)
 99         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitJmlMethodDecl(JmlAssertionAdder.java:15127)
100         at org.jmlspecs.openjml.JmlTree$JmlMethodDecl.accept(JmlTree.java:1261)
101         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
102         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
103         at org.jmlspecs.openjml.esc.JmlAssertionAdder.visitJmlClassDecl(JmlAssertionAdder.java:13784)
104         at org.jmlspecs.openjml.JmlTree$JmlClassDecl.accept(JmlTree.java:1174)
105         at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
106         at org.jmlspecs.openjml.vistors.JmlTreeScanner.scan(JmlTreeScanner.java:67)
107         at org.jmlspecs.openjml.esc.JmlAssertionAdder.convert(JmlAssertionAdder.java:1414)
108         at com.sun.tools.javac.main.JmlCompiler.rac(JmlCompiler.java:610)
109         at com.sun.tools.javac.main.JmlCompiler.desugar(JmlCompiler.java:454)
110         at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:898)
111         at com.sun.tools.javac.main.JmlCompiler.compile2(JmlCompiler.java:712)
112         at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:867)
113         at com.sun.tools.javac.main.Main.compile(Main.java:553)
114         at com.sun.tools.javac.main.Main.compile(Main.java:410)
115         at org.jmlspecs.openjml.Main.compile(Main.java:581)
116         at com.sun.tools.javac.main.Main.compile(Main.java:399)
117         at com.sun.tools.javac.main.Main.compile(Main.java:390)
118         at org.jmlspecs.openjml.Main.execute(Main.java:417)
119         at org.jmlspecs.openjml.Main.execute(Main.java:375)
120         at org.jmlspecs.openjml.Main.execute(Main.java:362)
121         at org.jmlspecs.openjml.Main.main(Main.java:334)
122 $PATH\src\network\RealNetwork.java:193: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
123             return groups.get(id).getRelationSum();
124                                         ^
125 $PATH\src\network\RealNetwork.java:248: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
126         Graph<AbstractPerson> newGraph = new Graph<>(personSet);
127       ^
128 $PATH\src\network\RealNetwork.java:249: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
129         return newGraph.isReachable(oriPerson, dstPerson);
130                        ^
131 $PATH\src\com\oocourse\spec2\main\Group.java:33: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
132       @          (\sum int j; 0 <= j && j < people.length && people[i].isLinked(people[j]); 1));
133                   ^
134 $PATH\src\com\oocourse\spec2\main\Group.java:38: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
135       @          (\sum int j; 0 <= j && j < people.length &&
136                   ^
137 $PATH\src\com\oocourse\spec2\main\Group.java:45: 注: Runtime assertion checking is not implemented for this type or number of declarations in a quantified expression
138       @ ensures (\exists BigInteger[] temp;
139                  ^
140 1 个错误
View Code

相关文章:

  • 2021-08-18
  • 2021-06-08
  • 2021-11-21
  • 2022-12-23
  • 2022-02-19
  • 2021-08-18
猜你喜欢
  • 2022-03-01
  • 2021-05-25
  • 2022-02-06
  • 2021-08-23
  • 2021-10-17
  • 2021-10-26
相关资源
相似解决方案