Title: Languages and Contracts
1Languages and Contracts
- Annatala Wolf
- 222 Lecture 8
2Study Guide
- The first half of this lecture discusses how Java
and C differ from the paradigm we use in
Resolve/C. While it will be useful for
understanding Closed Lab 8, dont bother studying
it. We will not test you on these topics. - We may test you on the second-half of this
lecture (dealing with contracts).
3Raw Pointers (C, C, C)
- // Make three pointers to Type objects.
- Type foo1 // all of these do the same thing
- Type foo2 // no default value its wild
- Type foo3 // this is the most common way
- // The reason its common if you declare
multiple - // objects, only the ones with in front are
pointers. - // (This is a dumb idea, but its in the
language.) - Type f, g, h // f is a Type pointer, but g and
h - // are regular stack-based objects
- // Create a new Type object on the heap.
- foo1 new Type // default constructor
- foo2 new Type(8, la!) // explicit
constructor - Type foo4 new Type // combine declare new
4Using Pointers (C, C, C)
- // Say class Foo has an operation declared
- // void (i.e. procedure) bar(int i)
- Foo quux new Foo()
- // Both of these calls are identical.
- // ( a-gtb is shorthand for (a).b )
- (quux).bar(3)
- quux-gtbar(3)
- // -gt is an example of syntactic sugar. It
- // is an shortcut that makes code easier to
- // type and more legible theres no down side.
5Arrays (C, C, C)
- // Make static arrays of things (directly on the
STACK). - int array110 // explicit, makes 10 ints
- int array2CONSTANT // CONSTANT must be
constant value - Foo array34210 // uses default constructor,
4210 - Foo array43 1,2,3 // uses Foo(int i)
constructor - Foo array53 Foo(1), Foo(2), Foo(3) //
same as above - // Make dynamic arrays of things (on the HEAP).
- int array6
- array6 new int10
- int array7 new intvariable
- Foo array8 new Foolots // uses default
constructor - // string literals are of type const char
- char name new char20
- name0 \0 // null char used to mark end
- input gtgt name // null slides to right of
array
6Using new and delete
- // With single items
- Foo f new Foo()
- ...
- delete f // No delete in Java.
- // With arrays
- char a new char50
- ...
- delete a // No delete in Java.
7Array Pitfalls (C, C)
- Arrays in C and C are of a primitive type (they
are not fully-fledged objects), which makes
them fast, but also unsafe. - You can go past the end of an array and not
realize it. This can lead to pointer-like
errors, even when the array is allocated on the
stack!
8Stack Buffer Overflow
- If the program running will allow you to go even
one byte past the end of a stack-based array, you
can overwrite the return pointer to code! - Then, the code returns to any place in memory you
wantso you can run your own code on a foreign
system! - This is one of the oldest exploits.
The Code
main()
sub()
The Heap
Malicious Code!
a012
3 leads here
goback
sub() input ltlt x input ltlt y ax y
gt 3 gt ( of your code) Hello I am Malicious
Code!
main()
Program Stack
(Terminal)
9Operator Overloading (C, C)
- C and C support operator overloading. Whereas
gt is syntactic sugar, operator overloading is
more like syntactic crack - Its very usefulbut easy to forget youre
calling an operation that can change its
arguments. - You have to break encapsulation to write operator
code in C, and put things into global scope. - If you use it all the time you end up having to
write overloaded operators for all your classes,
and often do more work than necessary.
10Assignment Confusion (C)
- The assignment operator in C can actually
mean four different things - Run the default copy constructor (shallow copy)
- Run the explicit copy constructor
- Try to copy the variable by default (shallow
copy) - Use the overloaded operator
- Even experienced C programmers can mistakenly
assume the wrong one happens. - Fortunately, C bans overloading , at least.
11Shallow Copy (any use of pointers)
- A shallow copy is a copy made by simply
iteratively copying all of the pieces. In C,
this means running one of the operations for
each data field of an object. - If an object contains pointers, or contains
objects that contain pointers, copying may fail
to work. You may end up with two objects sharing
the same data! ?
12Function Pointers (C, C)
- In C and C you can also declare pointers to
functions. Then you can dynamically select a
function without using polymorphism. - While there are legitimate uses of function
pointers, they can be used in very bad ways and
result in inscrutable errors. For this reason
theyre not used in C or Java.
13Java References
- Java primitive types are simple variables.
- So copies, and tests equality, as expected.
- Java references are pointers.
- Using aliases, and tests for aliasing.
- The dot operator dereferences automatically.
- int i 1, j 1
- System.out.println(i j) // true
- String name1 new String(Bob)
- String name2 Bob // same as
above - System.out.println(n1 n2) // false
- S.out.println(n1.equals(n2)) // true
- n1 n2 // ALIAS n1 to n2 now, n1 n2!
- i j // COPY value of j into i
14Differences in Java Parameters
- All parameters are passed by value (by copy) in
Java. The reason this doesnt attempt to copy
objects is that all object variables are secretly
pointers to objects. - This means you can modify an object in a method,
but you cant reassign it with . - So you cant do the swapping paradigm thing
directly. - The workaround wrap your object in a one-element
array (this is an ugly kludge), or make a swap
operation for the class that needs it (by
exchanging the pointers to data members).
15Java client call for foo(a, b)
- void foo(Thingy x, Thingy y)
- // This changes the original a.
- x.changeStuff()
- // Does not change a! In fact, now we cant
see - // a anymore. Using means x points
elsewhere. - x new Thingy()
- // But these DO change b, since we use y to
get - // to y0, which is a reference stored in y.
- y0 x /or/ y0 new Thingy()
- // But if we do this, we cant see b anymore.
- y new Thingy1
16C client calls foo(a, b, c)
- // z is passed by copy, which can fail, or take a
long time. - void foo(Thingy x, Thingy y, Thingy z)
- x.changeStuff() // Changes the value of a.
- y-gtchangeStuff() // Changes the
value of b. - z.changeStuff() // Does not modify
c! - // This runs and tries to copy, but copy
might be shallow. - x y
- // This points y to the address of x. It
might work. - // But we cant see b anymore.
- y x
- // This would also stop us from seeing b
anymore. - y new Thingy()
- // Memory leak here, from ys non-deleted
object!
17Stack vs. Heap
- In Java, primitive variables go on the stack by
default and objects go on the heap. - However, Javas optimizer (as of 1.6) can put
things onto the stack for speed. You cant
explicitly control it on a case-by-case basis
(unlike C, C, and C).
18Example of C Pointers
- In this typical example, what might go wrong?
(Often we use a pointer Item typethink why.)
- public
- void add(Item item)
- Node temp new Node
- temp-gtnext first
- temp-gtdata item
- first temp
- size
-
- ...
- class SetltItemgt
- private
- struct Node
- Item data
- Node next
-
- Node first
- int size
19Copying Paradigm
- Since you cant always safely copy objects in C
(and moreover, since it may be very expensive),
collections are generally used by passing a
pointer in. - The downside of this the pointer is usually
passed by copy, and not changed (in Java, this is
the only way). The client can access an object
inside the collection! - Its up to the client to stop using the alias
(hopefully). -
- map.define(key1, value1) // If were not
careful, - map.define(key2, value2) // we can ruin our
map - key1.copyFrom(key2) // map now has two equal
keys!
20Polymorphism
- In C-style languages, most concrete-to-concrete
decoupling is performed through polymorphism
rather than templates. - The idea is simple. You can store an object of a
subtype (a type that inherits or extends another
type) in a pointer of the supertype. Example, in
Java - / foo has two types! Its static type is Fruit
- but its dynamic type is Pear (a subclass). /
- Fruit foo new Pear() // a Pear is a Fruit
21How Polymorphism Works
- Since you can store a subclass object in a
superclass pointer, you can pass it to anything
using the superclass. - Each type of fruit may have its own
implementation of tasteMe(). If so, you can
write code so that the right version of tasteMe()
runs based on dynamic type! - // You can pass foo, since foo is a Fruit!
- void nomNom(Fruit f)
- // if f is a Pear, calls Peartaste()!
- f-gttaste() // taste is specd in Fruit
22Polymorphism Example
- This is similar to how we use templates to select
different implementations of a type, except you
can do it at runtime (so its even more
flexible). - SetltIntegergt set null // static type
- // Select which version of Set to use at runtime!
- if (weWantHashSet)
- set new HashSetltIntegergt() // dynamic type
- ...
- ltEgt void updateSet(SetltEgt co) // takes a Set
- co.add(thing) // add() is specified in Set,
- // but it runs HashSets
version!
23Thinking about Contracts
- For the second half of this lecture, were going
to shift gears and discuss issues related to
contracts and correctness of software in
component-based systems. - With the exception of the historical details
(which are there for background), you should
expect to be tested on the concepts discussed in
these slides.
24Famous Dead Computery Dudes
- Georg Cantor, 1845-1918
- inventor of set theory (which led to computation)
- reasoning about infinity drove him completely
insane - Kurt Gödel, 1906-1978
- logician who proved the incompleteness of math
- afraid of being poisoned starved himself to
death - Alan Turing, 1912-1954 (died young age 41)
- considered the father of computer science AI
- helped Allies win WWII by cracking the Enigma
code - convicted of being gay, chemically castrated
- suicide by cyanide, as a result
25Turing-Stuff
- Turing, in particular, had a profound effect on
computer science - Turing machine a minimal computation model upon
which languages like C are based - Turing-complete a set of conditions which prove
the completeness of a language (whether you can
write any program in it) - Turing test a test to see if machines have
become intelligent - Turing award the Nobel of computer science
26C.A.R. (Tony) Hoare
(Still alive!)
- Tony is not dead yet!
- I will need to update my slides when he dies.
- Inventor of Quicksort (1960)
- Quicksort is the most widely-used sorting
algorithm. - Creator of Hoare logic
- A means for reasoning about the behavior of
programs at the programming language level. - Yes. That is how you pronounce it. Im sorry.
- He won the Turing award in 1980 for his many
contributions to computational theory (specific
to correctness in programming languages).
27Questions Tony Asked
- How can we tell what a loop will do?
- global_procedure Concatenate(
- alters Queue_Of_Integer q1,
- consumes Queue_Of_Integer q2
- )
- /! ensures
- q1 q1 q2
- !/
28Examining the Loop
- If we only look at what changes, then we cant
tell what the result will be. Tony realized
something was missing - procedure_body Concatenate(
- alters Queue_Of_Integer q1,
- consumes Queue_Of_Integer q2
- )
- while (q2.Length() gt 0)
- /!
- alters q1, q2
- (doesnt tell us q1 will q1
q2...) - !/
-
29Showing Termination
- One thing thats missing is we need to prove the
loop will end someday -
- while (q2.Length() gt 0)
- /!
- alters q1, q2
- decreases q2
- (Since q2 is an ordinal gt 0, this
- means it must eventually terminate.)
- !/
30Loop Unrolling
- Loops can be unrolled, but this only works well
if you can do it a pre-set number of times. - Loop unrolling is not useful for proving
correctness for a loop that runs an indeterminate
number of times. - while (q2.Length() gt 0) C becomes...
- if (q2.Length() gt 0)
- C
- if (q2.Length() gt 0)
- C
- if (q2.Length() gt 0)
- C
- // ...aaaaaand so on, until the loop stops
running.
31The Insight Invariance
- The only way to prove that a loop does what it
says it will do is to identify the right
invariant. We must find something that doesnt
change each time the loop runs. - // trying to prove q1 q1 q2...
- while (q2.Length() gt 0)
- /!
- alters q1, q2
- maintains ???
- decreases q2
- !/
32Loop Invariants
- Loop invariants must be true each time the loop
begins, including - just before the first time the loop runs
- prior to every iteration of the loop
- right after the loop finishes for the last time
- A good loop invariant must also prove that the
loop does what its supposed to do. - This happens when it is combined with the
negation of the loop condition.
33Example
- In loop invariants, we use to mean the value
at the start of the loop (vs. the end of the
loop). - But technically, it should be true when comparing
any two iterations of the loop, in either
direction! - while (q2.Length() gt 0)
- /!
- alters q1, q2
- maintains q1 q2 q1 q2
- decreases q2
- !/
34Proof!
- while (q2.Length() gt 0)
- // maintains q1 q2 q1 q2
- Now we can prove that the loop does what its
supposed to! - when the loop terminates, q2.Length() must be
equal to zero (the opposite of the while
condition) - we know it terminates because decreases q2
- q1 q2 q1 q2 and q2 0 ? q1 q1
q2!
35Limitations
- Note that a loop invariant does not let you trace
a single iteration of a loop. You only trace the
whole thing at once. - while (q2.Length() gt 0)
- while (q2.Length() gt 0) object Item elem
- object Item elem if (q2.Length() gt 1)
- q2.Dequeue(elem) q2.Dequeue(elem)
- q2.Enqueue(elem) q1.Enqueue(elem)
- q2.Dequeue(elem)
- q1.Enqueue(elem)
- else
- q2.Dequeue(elem)
- q1.Enqueue(elem)
-
-
Both of these implement the previous loop
invariant, but they dont do the same thing on
each iteration.
36Trouble with optimizations
- If we store the length of the queue ahead of time
in a separate counter, maybe it will run
faster(?), but it may make it more difficult to
write the loop invariant (and harder to trace the
code in general). - object Integer counter q2.Length()
- while (counter gt 0)
- /! alters q1, q2, counter
- maintains
- q1 q2 q1 q2 and
- counter q2
- decreases counter !/
37Another example
- What would a loop invariant be for this loop?
(Dont get into groups, but try to come up with
the answer) - procedure_body Reverse(
- /! ensures s reverse(s) !/
- alters Stack_Of_Item s
- )
-
- object Stack_Of_Item temp
- while (s.Length() gt 0)
- // what is maintained each iteration?
- // also what alters and decreases?
-
- temp s
-
38Loop invariant for Reverse()
- procedure_body Reverse(alters Stack_Of_Item s)
-
- object Stack_Of_Item temp
- while (s.Length() gt 0)
- /!
- alters temp, s
- maintains rev(s) temp rev(s)
temp - decreases s
- !/
-
- temp s
-
- Note that we can trace Reverse() without using
the code. The loop invariant is enough to trace
the entire loop.
39Mystery code
- while (q.Length() gt 1)
- object Boolean left, right, combined
- q.Dequeue(left)
- q.Dequeue(right)
- combined (left ! right)
- q.Enqueue(combined)
-
- What does this loop do? It may not be obvious!
The easiest way to tell what it does is to find a
loop invariant. Get into groups of 2-4 and write
one. - Hint Think about how many true and false
values are in the queue each iteration. (You may
need to use modulus.) What doesnt change from
iteration to iteration?
Get into groups of 3-4 and try to find a loop
invariant for this loop (to figure out what it
does)!
40Solution
- The loop maintains the parity of true entries.
If there are an odd number of true in the
queue, there will always be an odd number of
true. If there are an even number of true,
there will always be an even number. - while (q.Size() gt 1)
- /!
- alters q
- maintains number of true values in q
mod 2 - decreases q
- !/
-
- // It leaves a single boolean variable in q that
- // answers Were there an odd number of true in
q?
41Tracing code with a loop invariant
- We expect you to be able to write loop
invariants, but you should also be able to trace
code with themeven if you dont have access to
the actual code. - This is similar to using an ensures clause to
tell what an operation will do - Tracing code with a loop invariant is simple.
Just figure out what must be true when the loop
is complete.
42Practice tracing code with invariants
- In groups of 2-4, trace the following loop.
- Assume that m 240 and n 25 what is answer?
- object Integer answer 0
- object Integer r m
- while (r gt n)
- /! preserves m, n
- alters r, answer
- maintains answer n r m
- and r gt 0
- decreases r
- !/
-
43Answer
- The loop performs integer division (below is one
possible implementation for the loop). - So answer m/n, and r is the remainder.
- while (r gt n)
- r r n
- answer
-
- With a loop invariant, we can trace the code in a
single step, without actually seeing the code.
44Specs vs. Code
- The specs we use in this class are rigorous
enough they could be considered a sort of code
in their own right. - However, theres a big difference between
specifications and actual code specs dont tell
you how something works. - Its easy to write a spec for something that
solves a math problem, even if the solution is
not known. - Its even possible to write specs for problems
that are known to be unsolvable! (like the
Halting Problem)
45Relational Behavior
- Specifications also allow us to describe a more
general kind of result than code does. - For example, in Partial_Map, the spec for
Undefine_Any() describes relational behavior it
allows multiple possible results to be produced
(even when totally deterministic). - It guarantees that the produced item exists in
the Partial_Map, but does not specify which item
it will pull out. - This has implications for testing of code! We
cant always automate the tests that determine
whether a kernel is correct, because it may be
the case that two totally correct implementations
return different results. - In other words, not all tests can be automated,
even if the test script can be.
46Formal Restrictions
- Specifications can also impose restrictions
either implicitly (when something is needed) or
explicitly. - In Resolve/C, Is_Equal_To() must be implemented
as a class member even though we think of it as a
utility class. So when its necessary, its
not something we fit into a template like Hash or
Are_In_Order. We spec it in the kernel instead. - If a class needs IET(), this will be
implementation-independent. For example,
Partial_Map needs IET(). Without IET(), there
would be no way to find a particular D_Item (for
Remove(), Is_Defined(), and Accessor). This is
true regardless as to which version of
Partial_Map you use.
47Contracts Lines of Evidence
- When writing code, how do you know what to check
and what to assume? You should always draw upon
all available evidence to avoid checking things
that cannot occur, but do check things that can
occur. - When you are writing an operation (as the
implementer), assume that the following are true
as the operation begins - requires clause for the operation
- convention holds (kernels and non-layered
extensions) - correspondence is correct (kernels and
non-layered ext.) - decreases clause was true (if called
recursivelybut all this tells you is it will
reach the base case eventually)
48Example SK3SLOB()
- Consider Sequence_Kernel_3 (from Closed Lab 4)
- // requires for Set_Length_Of_Before ? 0 lt pos
lt before_stack after_stack - local_procedure_body Set_Length_Of_Before (
- alters Stack_Of_Item before_stack,
- alters Stack_Of_Item after_stack,
- preserves Integer pos
- ) ...
- correspondence
- self reverse (self.before) self.after
- // requires clause for Sequence ? 0 lt pos
lt self - function_body Item operator ( preserves
Integer pos ) - Set_Length_Of_Before (selfbefore,
selfafter, pos) - return selfaftercurrent
How do we know its safe to call
Set_Length_Of_Before() without checking the
lengths of the stacks against pos?
49We want to prove 0 lt posSLOB lt beforeSLOB
afterSLOB
- requires 0 lt pos lt self
- correspondence self reverse (self.before)
self.after - parameter pass posSLOB pos beforeSLOB
self.before afterSLOB self.after - basic string facts reverse (string)
string s t u ? s t u - 2 3 self reverse(beforeSLOB) afterSLOB
- 1 3 0 lt posSLOB lt self
- 4 5 self reverse(beforeSLOB)
afterSLOB - 4 7 self beforeSLOB afterSLOB
- 6 8 0 lt posSLOB lt beforeSLOB
afterSLOB - weaken 9 0 lt posSLOB lt beforeSLOB
afterSLOB
50What to Learn
- We wont ask you to prove contracts formally.
However, you need to be able to understand and
explain the logic behind contracts in plain
English. - We can call Set_Length_Of_Before() without
checking its requires clause because the
requires clause for Accessor, combined with the
correspondence, tells us enough to be certain
that the requires for SLOB() is met. - You should assume the requires clause for the
operation you are writing (not the ones you
call), and the convention and correspondence,
each hold when the operation begins.
51Correctness
- What does it mean for code to be correct? As
weve seen throughout the quarter, my code
works does not imply it is correct - It may only work for the cases youve tested, and
testing is not capable of handling all (it can
catch the presence of bugs, but not an absence of
bugs). - Also, when we work with component-based software,
for code to be correct it has to work when paired
with different components or when modified by
someone who maintains the code.
52Why Concrete-Concrete Dependencies are Bad
- Consider the following procedure
- /! requires ASCII(c) ! 255
- ensures ASCII(c)1 ASCII(c) !/
- procedure_body foo( alters Character c )
- // ???
-
- We can actually test every test case (there are
only 255 to test). If it works in every case,
and its deterministic (no randomness), is it
correct?
53What if this is how foo() works?
- /! requires ASCII(c) ! 255
- ensures ASCII(c)1 ASCII(c) !/
- procedure_body foo( alters Character c )
- bar(c)
-
- /! requires ASCII(c) ! 255
- ensures ASCII(c) ! ASCII(c) !/
- procedure_body bar( alters Character c )
- c
Can you see the problem here? Why does foo()
work right now? Is there a situation in
component-based programming where foo() could
break the system?
54Correctness Defined
- Correct when code does what it says it will do
if its requires (including convention, etc.) is
true, based on the contracts of the operations it
calls. - If code depends on other code (rather than
specs), it will fail when coupled with different
components, or if a component is updated!