CSCI 5535: Homework 5 solution

1. (20 points) Consider the following code fragment:
      PROCEDURE f()
          TRY
             g();
           EXCEPT
              E1 =>  <S1>
            END

      PROCEDURE h()
          TRY
             g();
           EXCEPT
              E2 =>  <S2>
            END

    PROCEDURE g()
        IF cond1 THEN
           RAISE E1;
        ELSE IF cond2 THEN
           RAISE E2;
        ELSE
           <S4>

    PROCEDURE Main()
        TRY
           f();
           h();
         EXCEPT
            ELSE => <S3>
          END

In the above code, don't worry about checked versus unchecked exceptions.  (a) Show the exception tables for the above code.  (b) Consider the following call sequence: Main->h->g and g raises exception E1.  Using the exception table, show how this exception is handled.

Answer:
(a) at the call to g in procedure f, the exception table is: (E1, <S1>), (*, Reraise)
       at the call to g in procedure h, the exception table is: (E2, <S2>), (*, Reraise)
        at the RAISEs in procedure g, the exception table is: (*, Reraise)
         at the two calls in Main, the exception table is: (*, <S3>)
(b) When g raises exception E1, it finds table (*, Reraise).  This pops off g's activation record and reraises exception E1 in its caller h
       The table there is (E2, <S2>), (*, Reraise), which causes h's activation record to be popped off the stack (due to the Reraise handler) and reraises the exception in caller Main
        According to Main's table, all exceptions are handled by <S3> so code <S3> is executed.

2.  (20 points) In class I presented a scheme for implementing exception handling for Java and Modula-3 (it's the same scheme you used in question 1 above).  Can this scheme be used if exceptions handlers have a dynamic scope as in PL/1.  Explain and give a supporting example.
Ans: the scheme presented in class cannot be used for exception handling with dynamic scopes since it assumes that one knows statically all the exceptions that can be raised at a program point.  For example, consider the PL/1 like code below:
    on condition E1 print "Hi"
     if b is true then
       on condition E1 print "Bye"
     end
      <S1>

In code <S1> we don't know statically what the handler of E1 is: it could be either the "Hi" or the "Bye" prints.  Thus, we cannot generate a table statically to map exceptions to exception handlers.
 

3. (20 points) Closures are difficult to implement efficiently when nested functions can escape.  Consider a language that supports nested functions but wants to keep their implementation efficient by prohibiting nested functions from escaping.  Describe the compile-time and/or run-time checks that are necessary in order to prohibit nested functions from escaping.  For each check you describe indicate clearly if it is a compile time or a run-time check.
Ans: At run-time, we need to check that the code does not store a nested function in a variable or return it from a routine.  This check is easy (and at compile time) if the assignment or return is explicit e.g. :
    x := nested_function;
     or RETURN nested_function.
 But if the return or assignment is indirect, then more work is needed.  Assume that all functions are passed as closures.   The Environment component of closures of global functions is empty and of nested functions is non-empty.
  Then if we see x := p, where x is a procedure typed variable, then we need to ensure at run-time that the closure pointed to by p has an empty "Environment" component (i.e., p->env is NIL).  Similarly, if we see RETURN p, where the enclosing function is declared to return a function, we need to ensure at run-time that the environment component of the closure pointed to by p is empty.

4.  (20 points) Write the following function:
       compare: ''a list * ''a list  -> bool
     "compare" takes a pair of lists, and returns true if the lists are identical and false otherwise.  Use continuations (callcc) in your code so that compare exits immediately if finds that the lists are not identical.
Ans:

fun compare_lists (l1, l2) =
  callcc(fn k =>
              let fun comp_aux (nil, nil) = throw k(true)
                       |  comp_aux(h::t, nil) = throw k(false)
                       |  comp_aux(nil, h::t) = throw k(false)
                       |  comp_aux(h1::t1, h2::t2) = if h1 = h2 then comp_aux(t1, t2) else throw k(false)
               in comp_aux(l1, l2) end)