Software Preservation Group of the Computer History Museum

Critical Mass Modula-3
compiler and runtime changes

Note: Not all these changes survived after Critical Mass cm3 was open-sourced.

Date: Tue, 08 Apr 1997 10:15:44 -0500
To: farshad@cmass.com
From: Bill Kalsow <kalsow@cmass.com>
Subject: Re: List of changes to the runtime
Cc: kalsow@cmass.com

Here's the list:  (as much as I can remember from my notes...)

   1)  Modules can be added to the system at runtime.  From C it looks
       like this:

            RTLinker__InitRuntime (argc, argv, envp, instance(*Win32 GUI*));
            RTLinker__AddUnit (Foo_I3);

       "InitRuntime" needs to be called once, before any calls to "AddUnit".
       The call to "AddUnit" above adds "Foo.i3" and anything it imports
       or that exports it to be added to the running system.  That means
       their types are registered, brands are checked, main bodies are
       called, etc...    ("Foo_I3" is a procedure generated by the compiler
       when it compiled "Foo.i3".)

       Of course, that means its possible to add types on the fly as well.
       The details are messier.

    2) Exceptions can be marked "implicit" by preceding their declaration
       with the <*IMPLICIT*> pragma.  An implicit exception can be raised
       by a procedure even though it's not named in the procedure's RAISES
       clause.  ***This changes the language semantics of RAISES clauses.***
       Also, the compiler does not warn about unhandled implicit exceptions.

    3) Checked runtime errors (NIL fault, array index out of bounds, ...)
       are signaled via the implicit exception "RuntimeError.E".

    4) NEW() raises RuntimeError.E if it cannot allocate more memory.
 
    5) The RTAllocator.New*() routines raise RTAllocator.OutOfMemory,
       a new, non-implicit exception.

    6) RTException.SetBackstop() can be used to override the default
       handling of "unhandled exceptions".  The default is to raise
       RuntimeError.E(RuntimeError.T.UnhandledException), and then if
       that exception isn't handled, to crash and burn...

    7) The layout of OBJECTs is compatible with COM.  The first word
       of the object is a pointer to its method list (vtable in COM-speak).
       The first word of the method table points to the procedure implementing
       the first method, and so on ...

    8) Unicode support has been added.

       There is a new builtin type, WIDECHAR.  WIDECHARs occupy 16
       bits of storage.  Like CHAR, WIDECHAR is an enumeration with
       compiler-recognized literals.  The new literals are like
       CHAR literals, but are preceded by "W".  For example, "W'A'"
       is the 16-bit WIDECHAR with the ordinal value 65.  Octal escape
       sequences in WIDECHAR literals must specify 16 bits with 6
       octal digits.  For example, W'\000101' represents W'A'.
       Hex escape sequences are also allowed, they are introduced
       with "\x" and must also specify 16-bits with 4 hex digits.  So
       one more time, W'A' can be specified as W'\x0041'.  Hex escape
       sequences are also allowed in CHAR literals.  There, they must
       specify 8 bits with 2 hex digits.  The hex version of 'A' is
       '\x41'.

       TEXT literals preceded by "W" are instantiated by the compiler
       with WIDECHAR characters.  For example, W"Hello World".  TEXTs
       are implemented as Modula-3 OBJECTs.  So, they can contain
       CHARs or WIDECHARs, or even a combination of the above.  For
       example, "Hello " & W"World" contains both types of character.
       The Text interface includes new procedures that allow
       clients to deal with CHARs or WIDECHARs as they wish.  For
       example, "Text.GetWideChar(t: TEXT; i: CARDINAL): WIDECHAR" is
       the parallel extension of "Text.GetChar".  The Text implementation
       automatically converts CHARs to WIDECHARs as needed by zero-extending
       the 8-bit values to 16-bit values.  It converts WIDECHARs to
       CHARs by dropping the high-order 8-bits of the 16-bit value.
       "Text.Length" returns the number of characters, not 8-bit bytes
       or 16-bit words.  "Text.Equal", "Text.Compare", and "Text.Hash"
       operate internally by extending all characters to their
       corresponding 16-bit values.

       Because TEXTs are objects, not REF ARRAY OF CHAR, they can
       be implemented in many more exotic forms.  For example, in the
       new system the concatentation of two texts doesn't copy their
       characters, it simply points to the the two texts.
       The new TextClass interface specifies what it takes to be a TEXT.

       "M3toC.TtoS()" no longer makes sense.  In general, it's not possible
       to convert an M3 TEXT to a C string without copying the characters.
       "TtoS" has been replaced by "SharedTtoS" and the corresponding
       "FreeSharedS".  If "SharedTtoS" detects a flat C-compatible
       literal, it returns the pointer to that string.  Otherwise, it
       copies the string and returns the address of the new storage.
       "FreeSharedS" releases any storage acquired by "SharedTtoS".

       The full set of libraries hasn't been converted.  Libm3 and m3core
       are fixed.  Trestle, VBTkit, Obliq, Netobj, ... have not.  Because
       of the automatic internal 8-bit<->16-bit conversions, these other
       libraries will continue to work, they will just ignore the high-order
       8 bits of any 16-bit characters in the TEXTs they happen to encounter.

    9) "Compiler" is a new built-in interface like "Word".
       "Compiler.ThisFile()" returns a TEXT containing the name of the
       source file containing the call.  "Compiler.ThisPath()" returns a
       path from the current directory to the source file containing the
       call.  "Compiler.ThisLine()" returns the source line number
       containing the call. "Compiler.OS" is the enumeration "{POSIX, WIN32}".
       "Compiler.ThisOS" is a constant Compiler.OS value indicating
       where the source was compiled.  "Compiler.Platform" is an
       enumeration, "{AIX386, ALPHA_OSF, ...".   "Compiler.ThisPlatform"
       is a Compiler.Platform constant indicating where the source
       was compiled.  "Compiler.ThisException()" returns a pointer
       to runtime information about the exception current being handled.
       Compiler.ThisException can only be called from a TRY-FINALLY
       or TRY-EXCEPT handler.

   10) The <*ASSERT*> pragma can carry user-defined messages.  The new
       syntax is:

          <*ASSERT condition [ ("," | WITH) msg ] *>

       Where "condition" is a BOOLEAN value as before and "msg" is
       a TEXT value.

   11) There is a <*DEBUG*> pragma with the following syntax:

          <*DEBUG condition [ ("," | WITH) msg {, msg} ] *>
          <*DEBUG [ msg {, msg } ] *>

       <*DEBUG*> pragmas may appear anywhere that statements may appear.
       If "condition" is present, it must be a BOOLEAN.  If "condition"
       is "TRUE" or missing, the "msg"s are printed.  

       "RTDebug.RegisterHandler()" can be used to override the default
       printing of debug messages.

   12) The built-in type MUTEX is implemented as an OBJECT with "acquire"
       and "release" methods.  Clients that override those methods can
       change the behavior of LOCK statements.

   13) "RTHeapRep.max_heap_size" can be set to limit the size of the
       heap.  If it is set to a non-negative value and the collector
       cannot find enough storage to satisfy an allocation request
       and the heap is already at least "max_heap_size" bytes, the
       heap will not be extended and the allocation request will
       fail.  Otherwise, the heap will be extended to satisfy the
       allocation request.

 - Bill
___________________________________________________________________
Bill Kalsow             E-mail: kalsow@cmass.com
Critical Mass, Inc.     WWW:    http://www.cmass.com/people/kalsow/