[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ooc-970425 & oo2c-1.2.2



What new?

Conformant vs non-conformat mode
In non-conformant mode 
o it's possible to compare two procedure values (variable or
  procedure) if their formal parameters match 
o WITH won't tamper with the interfaces of enclosing procedures 
o the redefinition of a type-bound procedure may extend the result
  type of the base procedure
The mode is selected by setting the pragma variable `ConformantMode'.
The default value is FALSE.

NEW will check for illegal length and out of memory
A length of zero is allowed, both for fixed length array declarations
and invocations of NEW.  A negative value will trigger a compile-rime
or run-time error.

Configuration file can be set through environment variable
oo2c and all its tools now check for the environment variable
OOC_CONFIG.  If it's present it'll supersede the hardcoded path to the
configuration file.  

New library module `Exception'
This module implements a mechanism for exception handling along the
lines of the one offered by Standard Modula-2.  Since it's a full
library module instead of a language feature it doesn't give quite the
comfort and security as M2 does.  Currently it's a stand alone module
without any connections to the rest of the run-time system, but I
intend to change HALT, ASSERT, run-time checks, and signals to raise
exceptions in the future.  Thanks go to Eric Nikitin for beating me to
implement this and for his help with ironing out the details.  Please
check if the attached test module works on your system, since it is
using some _very_ tricky ANSI-C constructions in a likewise _very_
tricky way.  I don't want to get any unpleasant surprises _after_ I've
changed the run-time system to depend on this mechanism.

There were some last minute changes to the specs of the Exception
module.  The description of the RAISE function didn't match the
implementation.  If you want to look at the interface of Exception,
use the attached definition module.  For all other purposes use the
sources that are part of the distributed tar files.  A simple test
program and its expected output are attached, too.

-- mva


Interface of `Exception':
------------------------------------------------------------------------
MODULE Exception [FOREIGN "C"; LINK FILE "Exception.c" END];
(*  Provides facilities to raise and handle exceptions.
    Copyright (C) 1997  Michael van Acken, Eric Nikitin

    This file is part of OOC.

    OOC is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.  

    OOC is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
    License for more details. 

    You should have received a copy of the GNU General Public License
    along with OOC. If not, write to the Free Software Foundation, 59
    Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
<* Warnings := FALSE *>


(* Exceptions are events that may require special processing by the user 
   program or by the implementation. 
   
   Exceptions may be raised by the computer's error-detection mechanisms,
   via calls to HALT or ASSERT, by the user program itself, or by actions
   external to the program.
   
   The primary use of this module is to provide error handling and allow
   programmer control over both system and language ( HALT and ASSERT )
   exception handling. The programmer may define handlers which can be
   used in place of those defined by the implementation.
*)

CONST
(* Constants for use with exceptions sources halt, assert, signal, and
   runtime should be provided.  Since signals are somewhat system
   dependent they should be delegated into a separate module.
   
   For example, for exception source `runtime',
  -- index out of range
  -- function without return
  -- invalid case
  -- type guard failed
  -- implicit type guard failed
  -- with guard failed
  -- value out of range
   and exception source `signal' (will be handled in module `Signals'):
  -- floating point error
  -- illegal instruction
  -- segmentation violation
  -- bus error
  -- abort
  -- breakpoint
  -- bad system call
  -- etc
*)

(*
Notes:
Wherever the word `thread' appears below it should be read as
`program' for the moment.  Multi-threading isn't supported yet. 

There are two states of program execution: normal and exceptional.
Until the first call to RAISE the program is in the normal execution
state.  After that it stays in the exceptional execution state until
ACKNOWLEDGE or RETRY is called.  

The restriction of PUSHCONTEXT that at most one context can be active
in a procedure allows an efficient implementation of the context stack
without falling back upon heap objects.

The state of all nonglobal variables that were modified after a
PUSHCONTEXT is undefined if the context is activated again by raising
an exception or calling RETRY.  The reason is simple: while the
compiler ensures that the evaluation of a piece of code delivers the
correct results in the end, it does not ensure that the state of an
interrupted computation is correctly reflected by the memory contents
at the time of the interruption.  An additional restriction affects
exceptions that weren't initiated by an explicit call to RAISE, i.e.,
failed run-time checks and external signals.  For these exceptions the
exact place of the raised execption, i.e., the set of intructions that
were completed before it was raised, is undefined.  The reason is that
a sequence of instructions as specified in the source code may be
evaluated in a different order or in an overlapped fashion in the
emitted machine code.  

Unlike Modula-2, the programmer is reponsible for managing the stack of
exception handlers.  The stack is primarily manipulated through the procedures 
PUSHCONTEXT and POPCONTEXT.  The only other action that changes the stack is
a raised exception while the program is an exceptional execution state: this
will pop the topmost context (that was responsible for the exception) from the
stack before moving control to the then topmost execution context.  Raising an
exception in the state of normal excection will _not_ change the stack.  
Therefore every call to PUSHCONTEXT must have exactly one matching call to
POPCONTEXT in the same procedure, assuming that the program parts in between
are completed without raising an exception.

If a stack underflow occurs when POPCONTEXT is called, an exception is raised.
If an execution context is left of the stack that doesn't correspond to a valid
procedure, i.e. a procedure doing a PUSHCONTEXT was left without doing a 
matching POPCONTEXT, activating the context by raising an exception will 
transfer the program into a completely undefined state.  Most likely it'll 
abort due to a segmentation violation or a comparable error, or the stack of
execution contexts is rolled back until a valid context is reached.  There is
no way to check for such a situation.  Any programmer should be aware that an
invalid context stack can cause considerable grief.
*)


TYPE
  Number* = LONGINT;
  Source* = POINTER TO SourceDesc;
  SourceDesc* = RECORD
  END;

VAR
  (* these two exception variables are associated to the standard
     predefined procedures HALT and ASSERT; HALT(n) is equivalent to 
     RAISE(halt, n, ""), and ASSERT(FALSE, n) to RAISE (assert, n, "") *)
  halt-: Source;
  assert-: Source;
  (* this exception source is used to report failed run-time checks: *)
  runtime-: Source;

PROCEDURE [PROC_ID=1] PUSHCONTEXT* (VAR source: Source);
(* Pushes the current execution context onto the exception handler
stack and sets `source' to NIL.  If the context is reactivated later by 
raising an exception, it will be set to the exception's source.
Only one context can be pushed per procedure at a time.  During a 
single procedure evaluation two successive calls to PUSHCONTEXT without
a POPCONTEXT in between are not allowed and will result in undefined
program behaviour.
Note: All nonglobal variables of the enclosing procedures that were
modified after the initial call to PUSHCONTEXT are undefined when the
context is activated again by raising an exception.  *)

PROCEDURE POPCONTEXT*;
(* Removes the exception handler on the top of the stack.  During the
execution of a procedure the dynamic number of calls to POPCONTEXT has
to balance the ones to PUSHCONTEXT.  If the stack is empty an exception
is raised.  If the program is in an exceptional execution state at the
point where POPCONTEXT is called, the exception is raised again, thereby
passing it along to the next higher exception handler.  *)

PROCEDURE RETRY*;
(* If the current thread is in the exceptional execution state, the
context on top of the stack of exception handlers is reactivated in
the state of normal execution; this will look as if the corresponding
call of PUSHCONTEXT will return again, with the parameter `source' set 
to NIL.  This allows the "normal" part to be re-executed.  Be very
careful when using this since all local variables of the enclosing
procedure that were modified after the initial call to PUSHCONTEXT are
undefined when activating the context again. 
If the current thread is in the normal execution state, calling
RETRY will raise an exception.  *)

PROCEDURE ACKNOWLEDGE*;
(* If the current thread is in the exceptional execution state, it
is placed back into the state of normal execution.  Otherwise an
exception will be raised.  Calling this procedure indicates that an
exception has been handled without retrying the "normal" part.  *)

PROCEDURE AllocateSource* (VAR newSource: Source);
(* Allocates a unique value of type Source.  If an unique
value cannot be allocated, an exception will be raised.  *)

PROCEDURE RAISE* (source: Source; number: Number;
                  message: ARRAY OF CHAR);
(* Associates the given values of source, number and message with the
current context and raises an exception.  This means that the current
thread switches into the exceptional execution state and activates
a program context from the stack of exception handlers. If the program
is in the normal execution state, the context on top of the stack is
selected.  If it's in an exceptional excecution state the stack is 
popped first.  Reactivating the excecution context will look as if the 
corresponding call to PUSHCONTEXT will return a second time, this time 
returning the first argument of RAISE in the variable parameter `source'.

The message may be truncated to a implementation-defined length. 
Using a value of NIL for the first argument will raise an exception. *)


PROCEDURE CurrentNumber* (source: Source): Number; 
(* If the current thread is in the exceptional execution state
because of the raising of an exception from source, returns the
corresponding number, and otherwise raises an exception.  *)

PROCEDURE GetMessage* (VAR text: ARRAY OF CHAR);
(* If the current thread is in the exceptional execution state,
returns the possibly truncated string associated with the current
context.  Otherwise, in normal execution state, returns the empty
string.  *)

PROCEDURE IsExceptionalExecution* (): BOOLEAN;
(* If the current thread is in the exceptional execution state
because of the raising of an exception, returns TRUE, and otherwise
returns FALSE.  *)

END Exception.







Test module for exceptions:
------------------------------------------------------------------------
MODULE ExceptTest;

IMPORT
  Exception, Out;

VAR
  src: Exception.Source;
  
PROCEDURE Counter (c: INTEGER);
  BEGIN
    Out.String ("Entering counter "); Out.Int (c, 0); Out.Ln;
    IF (c = 0) THEN
      Exception.RAISE (src, 1, "[ExceptTest] Counter reached zero")
    ELSE
      Counter (c-1)
    END;
    Out.String ("Leaving counter "); Out.Int (c, 0); Out.Ln
  END Counter;

PROCEDURE Test1;
  VAR
    e: Exception.Source;
  BEGIN
    Out.String ("Entering Test1"); Out.Ln;
    Exception.PUSHCONTEXT (e);
    IF (e = NIL) THEN  (* normal execution *)
      Counter (3)
    ELSE  (* exception was raised *)
      Out.String ("Caught exception"); Out.Ln;
      Exception.ACKNOWLEDGE
    END;
    Exception.POPCONTEXT;  (* clean up or be damned *)
    Out.String ("Leaving Test1"); Out.Ln
  END Test1;

PROCEDURE Test2;
  VAR
    e: Exception.Source;
  BEGIN
    Out.String ("Entering Test2"); Out.Ln;
    Counter (3);
    Out.String ("Leaving Test2"); Out.Ln
  END Test2;

BEGIN
  Exception.AllocateSource (src);
  Test1; Out.Ln;  (* raise & catch exception *)
  Test2; Out.Ln;  (* raise exception without installing handler *)
END ExceptTest.





Expected output of the above module:
------------------------------------------------------------------------
Entering Test1
Entering counter 3
Entering counter 2
Entering counter 1
Entering counter 0
Caught exception
Leaving Test1

Entering Test2
Entering counter 3
Entering counter 2
Entering counter 1
Entering counter 0
## 
## Unhandled exception (#1)
## [ExceptTest] Counter reached zero
##