Lecture_22_ExceptionHandling
Computation Abstractions
and
Exception Handling
CSCI 3136: Principles of Programming Languages
Agenda
• Announcements
• Lectures are being recorded
• Assignment 10 is due Monday, July 26
• Test 3, 9:00am, Thursday, July 29
• Student Rating Instruction is open
• Readings: Read Chapter 6.6, 9
• Lecture Contents
• Motivation
• Parameters and Arguments
• Applicative and Normal Order Evaluation
• Introduction to Exceptions
• Languages Support
• Exception Propagation
• Exception Implementation
7/14/21 10:25 AM 2
Keywords
Computation Abstractions
• Function
• Procedure
• Method
• Subroutine
• Caller
• Callee
• Evaluation
Parameters
• Pass-by-value
• Pass-by-reference
• Pass-by-value/result
• Pass-by-sharing
• Read-only
Arguments
• Applicative Order Evaluation
• Normal Order Evaluation
• Stack
Return Values
• R-value
• L-value
Exception
• Handler
• Propagation
• Raise
• Throw
• Catch
• Protected block
• try
• catch
• finally
• Scope
• Map
• Handler Stack
• Program Stack
• Stack frame
2021-07-14 10:25 AM 3
End of this Part
CSCI 3136
2021-07-14 10:27 AM 4
Motivation
CSCI 3136
2021-07-14 10:27 AM 5
Motivation
• We take functions, procedures, methods, and
subroutines for granted
• We learn them in 1st year and then use them
• How we use them is dictated by
• Scope (Static or Dynamic) ✅
• Binding (Deep or Shallow) ✅
• Parameters (what the functions expect)
• Arguments (what is passed in)
• Evaluation (what takes place inside)
• Return (L-values, R-Value, Composite, etc)
7/14/21 10:25 AM 6
A Function Is …
• Also known as a subroutine, procedure, method, etc.
• A piece of code that
• Specifies what parameters it is expecting to be called with (0+)
• Is called from somewhere in the program by the caller who provides arguments to be
bound to parameters
• When called, the callee performs a computation using the arguments provided by the
caller
• Optionally returns a value
• Optionally generates side-effects
• Example:
def fact( n = 1 ):
if n < 2: return 1 else: return n * fact( n – 1 ) fact( 42 ) • What does this do? It returns a big number. J 7/14/21 10:25 AM 7 Recall: Before the Call Caller • Pushes arguments on the stack • Pushes a dummy return value (optional) • Executes call instruction call foo • Pushes return address on stack • Jumps to subroutine (callee) Arg n … Arg 2 Arg 1 Return Value Return Addr … … … … … … … Stack 7/14/21 10:25 AM 8 Recall: During the Call Callee • Saves base pointer • Allocates local variables • Allocates temporary variables • Saves registers • Performs body of subroutine • Restores registers • Destroys local and temp variables • Returns ret • Pops return address off stack • Jumps to return address Arg n … Arg 2 Arg 1 Return Value Return Addr Local var 1 Local var m Local var 2 … Saved reg t Base Pointer … Temp var 2 Temp var r Temp var 1 … Saved reg 2 Saved reg 1 … … … … … … … Stack 7/14/21 10:25 AM 9 Recall: After the Call Caller • Pops arguments off the stack Arg n … Arg 2 Arg 1 Return Value … … … … … … … Stack 7/14/21 10:25 AM 10 End of this Part CSCI 3136 2021-07-14 10:25 AM 11 Arguments and Parameters CSCI 3136 2021-07-14 10:25 AM 12 Parameters and Arguments • Parameters are the variables specified by the function declaration, which are visible during the call E.g. here is a function declaration in Swift func choose( m:Int, n:Int = 1 ) -> Int { …
• Specifies two parameters: m and n of type Int
• The second parameter has a default value
• Arguments are the values passed by the caller to the callee
E.g. here is a function call in Swift
marbles = choose( 42, 13 )
• Passes two arguments to choose(): 42 and 13
• During the call, m is bound to 42 and n is bound to 13
7/14/21 10:25 AM 13
L-Values and R-Values
• Variables, arguments, expressions, and parameters can
be interpreted in two different ways
• R-Value : A value to be stored
• L-Value : A location where a value is to be stored
• For example, for a variable num:
• If we use num in an expression (2 + num), num is
interpreted as value
• If we use num in an assignment num = 2, num is
interpreted as a location
• In general:
• Expressions that yield values are referred to as r-values
• Expressions that yield memory locations are referred to as l-
values
7/14/21 10:25 AM 14
Parameter Modes
• Parameter modes is how the arguments are actually passed
to the function
• The standard modes are
• Pass-by-value: the r-value of the variable or expression is passed
• Used in C
• Pass-by-reference: the l-value of the variable or expression is
passed
• Used in Lisp, Smalltalk, Ruby, available in C++
• Other modes are:
• Pass-by-value/result: pass by value, but copy the value from the
parameter back to the variable that was passed
• Used in Algol W, Ada (in out)
• Pass-by-sharing: Similar to pass by reference. The object can be
modified, but reference cannot
• Used in Java for Composite types
• Read-only: Are passed by reference but cannot be modified
7/14/21 10:25 AM 15
Pass By Value
• The value of the expression is bound to the parameter (copied),
and passed to the function
• Modifications to the value are not visible outside of the function
int increment(int val) {
val++;
return val;
}
int i = 42;
int j = increment(i);
printf(“%d %d\n”, i, j);
• The output is: 42 43
7/14/21 10:26 AM 16
Pass By Reference
• The location (reference) of the value of the expression is bound to
the parameter (copied), and passed to the function
• Modifications to the value are visible outside of the function
int increment(int& val) {
val++;
return val;
}
int i = 42;
int j = increment(i);
printf(“%d %d\n”, i, j);
• The output is: 43 43
7/14/21 10:26 AM 17
Pass-by-Reference vs Pass-by-Value
• Pass-by-value
• Easier to understand (fewer side-effects)
• Efficient for primitive values (integer, floats, characters)
• Changes are not propagated back from the call
• Inefficient for large objects
• Pass-by-reference
• Used to pass large or complex data structures
• Changes to parameters are reflected in arguments
• Greater care needs to be taken during recursion
• Question: When should arguments be evaluated?
• At call?
• At use?
7/14/21 10:26 AM 18
Applicative and Normal Order of Evaluation
• Applicative-order evaluation
• Arguments are evaluated before a subroutine call
• Default in most programming languages
• Normal-order evaluation
• Arguments are passed unevaluated to the subroutine
• The subroutine evaluates them as needed
• Useful for infinite or lazy data structures that are computed
as needed
• Examples: Macros in C, Haskell, special forms in Scheme
• Fine in functional languages
• Problematic if there are side effects
• What if argument is not always used?
• Potential for inefficiency
• What happens if argument is passed to other subroutines?
7/14/21 10:26 AM 19
Arguments evaluated
before cons is called
Arguments evaluated
before cons is called
Normal Order Evaluation Example
• In Scheme there are two kinds of functions:
• Regular functions: e.g., +, cons, cdr, display
• Special forms: e.g., lambda, define, let, if, cond,
etc
• Regular functions use applicative order
• All arguments are evaluated before function is called
• E.g., (cons (+ a b) results)
• Special forms use normal order
• Arguments are passed unevaluated to the function
• E.g., (if (< a b) a b)
7/14/21 10:26 AM 20
Either a or b
is evaluated
Either a or b is
evaluated inside if
Return Values
• In most languages functions typically return r-values
• A value that can be assigned to a variable or used in an
expression
• Some languages, such as C++, allow functions to return
l-values (locations of the value)
• Return of l-values can be simulated in most languages
• Using pointers in C
• Returning references in Java
• Etc.
7/14/21 10:26 AM 21
int a[10];
int& f(int i) {
return a[i % 10];
}
…
for (int i = 0; i < 100; i++)
f(i) = i;
The & makes all
the difference
End of this Part
CSCI 3136
2021-07-14 10:26 AM 22
Exception Handling
CSCI 3136
2021-07-14 10:26 AM 23
Exception Handling
• Things go wrong (bleep happens), we need to handle it
gracefully
• Exception are unexpected or abnormal conditions during
execution
• Generated automatically in response to runtime errors
• Raised explicitly by the program
• Exception handling is needed to
• Perform operations necessary to recover from the exception
• Terminate the program gracefully
• Clean up resources allocated in the protected block
• Exception handling allows the programmer to
• Specify what to do when an error occurs during program run-time
• Separate the common path code from the error handling code
7/14/21 10:26 AM 24
Exception Handling Syntax
• Syntax for catching and handling
exceptions tends to be similar
• A protected block comprises 3
parts:
• try : the common path code to
be executed
• catch : exception handlers for
each exception to be caught
• finally : an optional ”clean-up”
handler that always runs after
the ”try” regardless of
whether an exception occurs
• Exceptions are raised (or thrown)
by a raise (or throw) statement
raise Exception_1(…)
try {
// common path
} catch ( Exception_1 e ) {
// Exception 1 handler
} catch ( Exception_2 e ) {
// Exception 2 handler
} ...
} else { // optional
// default handler
} finally { // optional
// clean up code
}
7/14/21 10:26 AM 25
Exception Handling Semantics
def main():
try:
parse()
except MemoryError as p:
print “Program too big!”
def parse():
try:
l = parseS()
if lookahead() != None:
raise ParseError( 'S' )
eval_result( top_ref, l )
except ParseError as p:
print "Syntax Error”
except EvalError as p:
print "Evaluation Error”
• An exception handler is
lexically bound to a block of
code
• When an exception is raised
in the block, search for a
handler in present scope
• If there is no matching
handler in present scope,
• The scope is exited (may
include block or
subroutine)
• A handler is searched for
in the next scope
Out of
Memory
7/14/21 10:26 AM 26
End of this Part
CSCI 3136
2021-07-14 10:26 AM 27
Exception Handling
Support
CSCI 3136
2021-07-14 10:26 AM 28
Language Support
• How are exceptions represented?
• Built-in exception type (Python)
• Object derived from an exception class (Java)
• Any kind of data can be passed as part of an exception
• When are exceptions raised?
• Automatically by the run-time system as a result of an abnormal
condition
e.g., division by zero, null dereference, out-of-bounds, etc
• throw or raise statement to raise exceptions manually
• Where can exceptions be handled?
• Most languages allow exceptions to be handled locally
• Propagate unhandled exceptions up the dynamic chain.
• Clu does not allow exceptions to be handled locally
• Some languages require exceptions that are thrown but not
handled inside a subroutine be declared (Java)
7/14/21 10:26 AM 29
Language Non-support
• Some languages do not
support exceptions
e.g., C
• Solution 1:
• Reserve a return value to
indicate an exception
• Solution 2:
• Caller passes a closure
(exception handler) to call
• Solution 3:
• In C, signals and setjmp /
longjmp can be used to
simulate exceptions
#include
int func(…) {
static jmp_buf env;
int i = setjmp(env);
if( i == 0 ) {
/* common path */
…
/* exception 42 */
longjmp(env, 42);
…
} else if( i == 42 ) {
/* handle exception 42 */
} else if( i == ? ) {
…
7/14/21 10:26 AM 30
Exception Implementations
• Options:
• Simple (Pay as you go)
• Location to Exception map (Pay on Exception)
• Hybrid
7/14/21 10:26 AM 31
End of this Part
CSCI 3136
2021-07-14 10:26 AM 32
Pay-as-You-Go Exception
Handling
CSCI 3136
2021-07-14 10:26 AM 33
Simple, Pay-as-You-Go
• Idea:
• The program uses a second stack,
called a Handler Stack (HS)
• When a protected block is entered,
a handler is pushed on the (HS)
• Pointer to the handler code
• Current stack frame (Program Stack)
I.e., referencing environment
Sound familiar?
• An optional exit (finally) handler may also be pushed
• If there are multiple exception handlers, these are
implemented using an if/elseif/… construct in a single handler
• When a protect block is exited, the handler is popped of the
stack
• Simple implementation is costly because handler stack
is manipulated on entry/exit of each protected block
…
…
…
…
…
…
…
H
Stack
7/14/21 10:26 AM 34
Simple, Pay-as-You-Go
def foo():
try:
bar()
except E1 as e:
# E1 handler
except E2 as e:
# E2 handler
def bar():
try:
…
except E3 as e:
# E3 handler
except E4 as e:
# E4 handler:
Handler
Stk Frame
Finally (opt)
…
…
…
…
…
…
…
H
Stack
if isinstance(E1, e):
# E1 handler
elsif isinstance(E2, e):
# E2 handler
else:
raise e
if isinstance(E3, e):
# E3 handler
elsif isinstance(E4, e):
# E4 handler
else:
raise e
Handler
Stk Frame
Finally (opt)
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
Stack
E1
7/14/21 10:26 AM 35
End of this Part
CSCI 3136
2021-07-14 10:26 AM 36
Pay on Exception
Exception Handling
CSCI 3136
2021-07-14 10:26 AM 37
Location to Exception Mapping
• A faster implementation (Pay on exception)
• Store a global map of code blocks (memory
addresses) to handlers
• Generated by compiler/linker
• On exception, index map with program counter to
get handler
• Still need to keep track of stack frames
• Each stack frame stores a pointer to most recent
protected block
7/14/21 10:26 AM 38
Pay on Exception
def foo():
try:
bar()
except E1 as e:
# E1 handler
except E2 as e:
# E2 handler
def bar():
try:
…
except E3 as e:
# E3 handler
except E4 as e:
# E4 handler:
Handler
Finally (opt)if isinstance(E1, e):
# E1 handler
elsif isinstance(E2, e):
# E2 handler
else:
raise e
if isinstance(E3, e):
# E3 handler
elsif isinstance(E4, e):
# E4 handler
else:
raise e
Handler
Finally (opt)
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
Stack
E1
7/14/21 10:26 AM 39
Comparison of the 2 Approaches
• Location-based Exception handling
• Handling an exception is more costly (search), but
exceptions rare
• No cost if no exceptions
• Cannot be used if the program consists of separately
compiled units and the linker is not aware of this
exception handling mechanism
• Hybrid Approach:
• Use a local map for each subroutine
• Store a pointer to a local map in subroutines stack frame
7/14/21 10:26 AM 40