一些重要的要求 (违反导致 desk reject)
源代码是软件工程的最重要产物。“分析源代码” 是许多软件工程研究/工具的基础。
软件工程研究中的常用工具:静态分析
Programs are meant to be read by humans and only incidentally for computers to execute. — D. E. Knuth
什么是代码?
什么是好的代码?
很多软件工程问题只需要源代码的浅层信息——例如一个不太深入的 “程序员喜欢给变量起什么名字” 研究。
文本分析
词法分析
"(a + b) * 2" =>
[ (SYM, '('), (ID, 'a'), (BIN_OP, '+'), (ID, 'b'), (SYM, ')'),
(BIN_OP, '*'), (INT, '2') ]
代码作为文本分析的一个应用是大家常用的 diff
Open Problem: 怎样才能让程序员能更愉快地阅读 diff? (这是一个很不错的软件工程研究问题)
“编辑距离最小” 的 diff 是个不错的 hack
A tree representation of the abstract syntactic structure of source code written in a programming language
一般编程语言都是用文法描述的
使用非常简单:clang -Xclang -ast-dump -fsyntax-only a.c
int f(int a, int b) { if (b == 0) return a; else { ... } }
|-FunctionDecl used f 'int (int, int)'
| |-ParmVarDecl used a 'int'
| |-ParmVarDecl used b 'int'
| `-CompoundStmt
| `-IfStmt
| |-BinaryOperator 'int' '=='
| | |-ImplicitCastExpr 'int'
| | | `-DeclRefExpr 'int' lvalue ParmVar 'b' 'int'
| | `-IntegerLiteral 'int' 0
| |-ReturnStmt
| | `-ImplicitCastExpr 'int'
| | `-DeclRefExpr 'int' lvalue ParmVar 'a' 'int'
| `-...
组织项目时,通常有 Style Guide,例如 Google 对 C++ 的要求:
table_name
, not tableName
)Lint 就是最早出现的代码风格扫描工具,绝大部分规则可以基于 token/AST 检查
Halstead’s software physics
一系列 metrics (统计量),与一些软件属性存在相关性
“我们不生产代码,我们只是互联网的搬运工”
- 代码克隆增加了一条隐藏的 specification: 一边改了,另一边也需要改
时间长了就忘记了 → 下次重构的时候 break 了 specification...
AST 是程序的结构化 “字面” 信息
代码格式化
Transcompiler (transpiler), source-to-source compiler
Mutation testing: 度量测试用例集的好坏
例子:µJava 实现
public class UOI_Writer extends MutantCodeWriter {
private static final String uop_string[] =
{ "++", "--", "++", "--", "~", "!", "+", "-" };
public void visit(Variable p) throws ParseTreeException { ...
if (mutant_op == UnaryExpression.PRE_DECREMENT || // --x
mutant_op == UnaryExpression.PRE_INCREMENT) { // ++x
mutated = "(" + op + p.toString() + ")"; // dump AST
} else {
mutated = "(" + p.toString() + op + ")";
}
...
“代码变形” 定义了一个搜索空间
一个很大的研究领域
局限 1: 对程序语义理解不足
局限 2: 元编程处理困难
#define FORALL(X) X(Tom) X(Jerry) X(Spike) X(Tyke)
#define PRINT(x) puts(#x);
// usage: FORALL(PRINT)
进一步分析有关程序执行的性质:某个分支是否可达?某个变量是否可能是某个特定的值?……
典型的应用场景
char buf[SIZE];
strncpy(dest, src, SIZE);
int len = strlen(dest); // insecure!
从程序 + 语义推导程序的性质
但精确计算程序的性质是 undecidable 的
静态分析:做出合理的近似 (over-approximation)
判断一对代码之间是否存在调用链
Call graph 可能比你想象的困难
如果一个 assertion failure 被触发了,到底是谁的锅?
foo();
bar();
baz();
assert(global == 0);
复杂的分析需要更简单的表示
(狭义的) 静态程序分析:一个巨大的研究领域
取决于你的研究问题
实际