Pengyu Nie


public class C {
public int factorial(int n) {
if (n > 0) {
return n * factorial(n - 1);
} else {
return 0;
}
}
}
factorial vs fact|orial.| token | kind |
|---|---|
public | KEYWORD |
class | KEYWORD |
C | IDENT |
{ | SYMBOL |
public | KEYWORD |
int | KEYWORD |
factorial | IDENT |
( | SYMBOL |
int | KEYWORD |
n | IDENT |
) | SYMBOL |
| … | … |
public class C {
public int factorial(int n) {
if (n > 0) {
return n * factorial(n - 1);
} else {
return 0;
}
}
}
The data format used by most static analysis tools, e.g.,
Concrete vs. Abstract

// lexer rules
IDENTIFIER : Letter LetterOrDigit* ;
Letter : [a-zA-Z$_] ;
LetterOrDigit : Letter | [0-9] ;
// parser rules
methodDeclaration
: typeTypeOrVoid identifier formalParameters
('[' ']')* (THROWS qualifiedNameList)? methodBody
;
typeTypeOrVoid
: typeType
| VOID
;
typeType
: annotation* (classOrInterfaceType | primitiveType)
(annotation* '[' ']')*
;
| tool | runtime | advantages |
|---|---|---|
| Tree-sitter; [grammars] | C | incremental; error-recovering; permissive |
| ANTLR; [grammars] | Java | precise; full-grammar; LL(*) |
Static = analyze the program without running it.
Kinds of data
Use cases
public int foo(int x, int y) {
int a = Math.abs(x);
int b = Math.abs(y);
if (a == b) {
return a;
} else {
int c = a - b;
while (a - b > 0) {
a++;
b--;
}
return a;
}
}

int x — def xint y — def yint a — def aMath.abs(x) — use x
Example: Static call-graph analysis must overestimate when there is dynamic dispatchdrawShape -> {Line.draw, Rectangle.draw, Circle.draw}

invoke instruction, (callee) at the beginning of methodSource code ver.
public static void drawShape(Shape shape) {
...
DynamicAnalyzer.logCaller("ShapeMain.drawShape");
shape.draw();
}
public void draw() {
DynamicAnalyzer.logCallee("Line.draw");
...
}
Bytecode ver. (most common case for compiled languages)
// ShapeMain
public static void drawShape(ca.uwaterloo.cs846.exp.Shape)
descriptor: (Lca/uwaterloo/cs846/exp/Shape;)V
Code:
0: aload_0
+ ldc "ShapeMain.drawShape"
+ invokestatic DynamicAnalyzer.logCaller
1: invokeinterface ca/uwaterloo/cs846/exp/Shape.draw:()V, 1
6: return
// Line (same for Rectangle / Circle)
public void draw()
descriptor: ()V
Code:
+ ldc "Line.draw"
+ invokestatic DynamicAnalyzer.logCallee
0: getstatic java/lang/System.out:Ljava/io/PrintStream
3: aload_0
sys.settrace
| PL | testing frameworks |
|---|---|
| Java | JUnit, TestNG |
| Python | pytest |
| C / C++ | GoogleTest, Catch2 |
| JS / TS | Jest, Vitest, Mocha |
| Go | built-in testing package |
| Rust | built-in cargo test, proptest |
// Dafny: spec + implementation in one place
method Abs(x: int) returns (y: int)
ensures y >= 0
ensures y == x || y == -x
{
if x < 0 { y := -x; } else { y := x; }
}
//@ requires n >= 0;
//@ ensures \result >= 1;
public int factorial(int n) { ... } // JML in a comment
/**
* Computes n! for non-negative integers.
*
* @param n a non-negative integer
* @return n factorial
* @throws IllegalArgumentException if n is negative
*/
public int factorial(int n) {
// base case
if (n == 0) return 1;
return n * factorial(n - 1);
}
def factorial(n: int) -> int:
"""Compute n! for non-negative integers.
Args:
n: a non-negative integer.
Returns:
n factorial.
Raises:
ValueError: if n is negative.
"""
...
