COMP1531
Static Verification
Presented by Atlassian
Hi, I'm Nick
In this lecture
- Software safety, risk, and other big picture items
- TypeScript introduction
- TypeScript syntax
Why should I pay attention?
- This lecture is the foundation of many courses to come
- If you can understand what we're talking about today, you will be covered on one of the cornerstones of modern software engineering
But first, risk and wrists
Good software design is all table setting.
When it comes to building software, sometimes you go in already having won or lost the fight.
Software Safety: Risk
- The biggest threat to your software is you, the programmer - because humans make mistakes
-
Software Safety vs Software Security
- Safety: protecting your software from accidental misuse
- Security: protecting your software from deliberate misuse
- Risk in software: from airbags to investor demos
- Software is too complex to be able to reason about once it reaches a certain size
Software Safety: Types of Errors
- Shift left: errors earlier are better than errors later
- What if you could know your code is correct without running it?
- Three types of errors:
- Compile errors (my code failed to compile because I missed a ;)
- Runtime errors (my code threw an exception because I tried to divide by 0)
- Logic errors (my code ran but gave the wrong answer)
Software Safety: Dynamic vs. Static Verification
-
Dynamic vs. Static Verification
- Dynamic - you have to execute the code in order to verify it
- Static - you do not have to execute the code in order to verify it
- Dynamic verification examples:
- Jest tests!
- Manually running your code and checking inputs/outputs
- Static verification examples:
- Compiling C code (runs GCC type checker)
- SAST (Static Analysis Security Testing) - analyses code for vulnerabilities
How can we statically verify our code?
- Turns out, lots of people are interested in this problem.
- What we really want is for code to be mathematically verifiable
- SENG2011 - Reasoning about Code - uses Dafny programming language, runs code through a "theorem prover" to verify semantic correctness
- Prove code by hand!
- Linux microkernel - 20 years, 480,000 lines of proof for 10,000 lines of C
- Mix of manual/automated:
- seL4 (COMP9242) - a verified microkernel (see next slide)
- In essence: turn the code into a mathematical model, and prove that the model is correct
How can we feasibly statically verify our code?
- Proving code correctness formally via mathematical models is very expensive :(
- Luckily, we have a proxy to verifying correctness since all code has types
- We can verify the types present in a program - this is called "type checking"
- If type checking passes, the program may/may not be correct
- If type checking fails, the program is definitely incorrect
- Take COMP3161 if you're interested in the mathematics underpinning type systems
Type Systems
- Most programming languages have a type system (C, JS, TS, Java, Python...)
- Languages that don't have a type system (Shell, Assembly, Visual Basic + older languages)
-
A statically typed language is where type checking is performed statically (compilation).
- The programmer must declare the types of variables using annotations
- A dynamically typed (AKA duck typed) language is where type checking is performed at runtime
- No extra annotations required
Type Systems
- Statically typed languages can have a "strong" or a "weak" type system depending on the level of enforcement of types.
- C is a statically typed language with a weak type system (types must be declared, and GCC runs type checking)
- More on this (I believe) in COMP6991
So, why TypeScript?
- JavaScript is a dynamically typed language, meaning it lets you make mistakes:
function manyString(repeat, str) {
let outString = '';
for (let i = 0; i < repeat; i++) {
outString += str;
}
return outString;
}
console.log(manyString('hello ', 5));
So, why TypeScript?
- Typescript is a language built on top of Javascript. Its job is to check the types in your program and outputs Javascript that is then run with node:
function sum(a: number, b: number) {
return a + b;
}
console.log(sum(1, 2));
Nick's 1531 Bug
- What's the error in the code below?
function nicksBug(
param1: string,
param2: number,
param3: number,
param4: number,
param5: string,
param6: string
) {
// ...
}
const bad = nicksBug('hello', 1, 44, 20, 58, 421, 'world')
Running TypeScript via ts-node
-
Typescript is another dependency we need to install:
-
npm install --save-dev typescript ts-node
-
-
Once this is installed, we can run our typescript code with the following command:
-
node_modules/.bin/ts-node 3.3_mycode.ts
-
-
Whilst ts-node does some type checking, it also runs the code. It's useful to have a way to "type check without running" that also checks a bit more strictly.
-
node_modules/.bin/tsc --noImplicitAny 3.3_mycode.ts
-
Running TypeScript via tsc
- We can also run type checking using tsc (TypeScript Compiler)
node_modules/.bin/tsc 3.3_mycode.ts
- This will actually convert the TS code into JavaScript!
- ts-node runs tsc && node under the hood
Type Annotations
-
Types are added to programs typically by putting the type name after a colon. We've seen that in our first example.
-
Typescript doesn't require you to put types on everything. It will infer types that it can, but sometimes it's unable to.
function hello(name: string) {
console.log(name);
}
function hello(name) {
// should be hello(name: string)
console.log(name);
}
const name = 'Yuchao'
TypeScript can't figure this out
TypeScript can figure this out
Types in TypeScript: Functions
function hello(name: string): string {
return `Hello ${name}!`;
}
Types in TypeScript: Unions
function printIfReady(ready: boolean | number) {
if (ready === true || (ready && ready !== 0)) {
console.log(`Ready! ${ready}`);
}
}
printIfReady(1);
printIfReady(2);
printIfReady(0);
printIfReady(true);
printIfReady(false);
Types in TypeScript: Collections (Arrays)
function create10List(item: string | number) {
const arr: Array<string | number> = [];
for (let i = 0; i < 10; i++) {
arr.push(item);
}
return arr;
}
Types in TypeScript: Aliases
type ListItem = string | number;
function create10List(item: ListItem) {
const arr: ListItem[] = [];
for (let i = 0; i < 10; i++) {
arr.push(item);
}
return arr;
}
Types in TypeScript: Optionals
// Note:
// end?: number
// = end: number | undefined
function substring(str: string, start: number, end?: number) {
let newString = '';
const modifiedEnd = end || str.length;
// ^ What about end ?? str.length
for (let i = start; i < modifiedEnd; i++) {
newString += str[i];
}
return newString;
}
console.log(substring('hayden', 0, 3));
console.log(substring('hayden', 2));
Types in TypeScript: Objects
type Person = {
name: string;
age?: number;
height?: number;
}
const person: Person = {
name: 'Yuchao',
};
person.age = 5;
Types in TypeScript: Any
// @ts-nocheck
function hello(name: any): any {
return `Hello ${name}!`;
}
function substring(str: any, start: any, end: any) {
return null;
}
type Person = any;
const person: Person = {
name: 'Yuchao',
};
Type Hierarchies
any
never
string
number
boolean
Custom objects/types
More on this in COMP2511, and even more in COMP3161
Static verification in industry
- Statically typed languages (TS, Java, Kotlin, C#) are very popular in industry
- Every piece of code that is merged into master/main is run through type checking (how?)
- Type checking isn't enough on its own
- More risky projects will have more layers of verification
Big ideas we talked about in this lecture, and where to from here
COMP1531 24T3: Static Verification
By npatrikeos
COMP1531 24T3: Static Verification
- 90