Languages and Contracts - PowerPoint PPT Presentation

1 / 54
About This Presentation
Title:

Languages and Contracts

Description:

Inventor of Quicksort (1960), the most widely-used sorting algorithm ... global_procedure Concatenate( alters Queue_Of_Integer q1, consumes Queue_Of_Integer q2 ... – PowerPoint PPT presentation

Number of Views:53
Avg rating:3.0/5.0
Slides: 55
Provided by: awo4
Category:

less

Transcript and Presenter's Notes

Title: Languages and Contracts


1
Languages and Contracts
  • Annatala Wolf
  • 222 Lecture 8

2
Study 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).

3
Raw 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

4
Using 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.

5
Arrays (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

6
Using 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.

7
Array 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!

8
Stack 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)
9
Operator 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.

10
Assignment 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.

11
Shallow 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! ?

12
Function 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.

13
Java 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

14
Differences 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).

15
Java 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

16
C 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!

17
Stack 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).

18
Example 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

19
Copying 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!

20
Polymorphism
  • 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

21
How 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

22
Polymorphism 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!

23
Thinking 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.

24
Famous 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

25
Turing-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

26
C.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).

27
Questions 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
  • !/

28
Examining 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...)
  • !/

29
Showing 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.)
  • !/

30
Loop 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.

31
The 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
  • !/

32
Loop 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.

33
Example
  • 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
  • !/

34
Proof!
  • 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!

35
Limitations
  • 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.
36
Trouble 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 !/

37
Another 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

38
Loop 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.

39
Mystery 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)!
40
Solution
  • 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?

41
Tracing 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.

42
Practice 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
  • !/

43
Answer
  • 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.

44
Specs 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)

45
Relational 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.

46
Formal 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.

47
Contracts 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)

48
Example 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?
49
We 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

50
What 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.

51
Correctness
  • 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.

52
Why 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?

53
What 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?
54
Correctness 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!
Write a Comment
User Comments (0)
About PowerShow.com