What is it? =========== This file is set of patches for the 01nov91 version of the Scheme->C system with the MCC01-01nov91.patches applied. These patches do 3 primary things: 1. Correct support for some machines and fix a minor bug reading floating point numbers. 2. Add support for the HP9000/700 (aka snake), the HP9000/300 (the 68k based HP s), the SONY-NEWS machine, and partial support for the IBM RS/6000. 3. Include some features that were developed here at MCC, including CATCH/THROW functions, opaque objects, a simple object system using opaque objects, and a different interface to external C functions. The MCC additions are documented more fully in the file MCC-additions in the top level directory after this patch is applied. As with the MCC01 patch, most of this work is not mine. I simply integrated things other people posted or sent to me. I was a member of the development team here at MCC that produced the MCC additions. I have verified this patch on the following machine/OS combinations: Sun4 - SunOS 4.1.1 Sun4 - SunOS 4.1.3 Sun3 - SunOS 4.1.1 DEC DS/5000-200 - ULTRIX V4.3 HP 9000/750 - HP-UX 8.05 How to use it. ============== This patch was generated against the 01nov91 version of Scheme->C with the MCC01-01nov91.patches patch applied. If this is a first time build, execute steps 1 and 2 in the MCC01-01nov91.patches file. Once you have a directory that contains the 01nov91 version with the MCC01 patch applied, follow the following steps to create a working version with the MCC02 patch applied. 1. cd into the top directory of the distribution. You should see something like this from "ls -F" if you executed the commands as given in the MCC01 patch file: APOLLO MIPS.MCC SUN3 makefile-tail APOLLO.MCC NeXT SUN3.MCC makefile-tail.MCC CHANGES NeXT.MCC VAX makefile.MCC I386 PRISM VAX.MCC scheme/ I386.MCC PRISM.MCC cdecl/ scrt/ ISC386IX README doc/ scsc/ ISC386IX.MCC SPARC gnuemacs/ test/ MIPS SPARC.MCC makefile xlib/ 2. Run the patch file through the patch program, like this ("this" assumes the patch file lives in the directory that is one level up from the top of the distribution): patch -s -p -b ".MCC02" ../MCC02-01nov91.patches Unless there is a problem with a file, the patch program will not print any messages. You may see some messages of the form: mkdir: cannot make directory `scrt': File exists This is OK, its just patch trying to make a directory that already exists. If any other problems were reported by patch you will have to correct the problem by hand. (I tried the patch on the above machines and it works for me :-) 3. Edit the top level makefile-tail (./makefile-tail) to fix up the definition of SRCDIR at a minimum. You may also want to change the various *DIR, *BIN, and *LIB macros that are appropriate for your environment. 4. Run a "make for" command, where ARCH is one of APOLLO HP300 HP700 I386 ISC386IX MIPS NeXT PRISM RS6000 SONYNEWS SPARC SUN3 TITAN or VAX. This will create the $(CPUDIR) directory, if it does not exist. It will then create a tree of symbolic links to all the necessary files in the distribution. [ If you can accept the values for CPUDIR that already exist in the makefile, you can just say "make for SRCDIR=$PWD" where $PWD has a valid pathname to the current directory] 5. cd into $(CPUDIR) which you set in #3 above or was generated by the setting of SRCDIR in #4 above. 6. Do a "make port" 7. Verify the build as in step #4 in the README file. I also suggest running more tests. This can be done like this: 4 >cd test 5 >../scrt/sci Scheme->C -- 01nov91jfb+MCC01 ... > (load "test.sc") MODULE form ignored (DEFINE-EXTERNAL TEST-ERRORS TESTCHK) TEST LOAD-TESTS "test.sc" > (load-tests) . lots of messages . > (test) ***** Begin Scheme->C Tests ***** . . ***** End Scheme->C Tests 0 Errors ***** #F > ^D 6 >make test 7 >./test 8 >cd .. You can also test the compiler by doing a "make test" in the test directory and then executing the "./test" program. It should produce the same results as the "(test)" above. 8. Because these patches modify some of the Scheme (.sc) files in the original distribution, the corresponding .c files need to be recompiled/rebuilt. This can be done with the command "make all". 9. Re-verify the system as in #7 above. More comments ============= Like I said, much of this work is not mine. I simply took all the patches others produced for the 01nov91 version and integrated them. Here is a list of the authors of the various patches I have integrated: SPARC - rec@arris.com (Roger Critchlow) I386 - rec@arris.com (Roger Critchlow) SUN3 - Mikael Pettersson APOLLO - Ray Lischner {uunet,decwrl}!mntgfx!lisch or lisch@mentor.com PRISM - Ray Lischner {uunet,decwrl}!mntgfx!lisch or lisch@mentor.com Sony News 3200 - Christian Queinnec queinnec@poly.polytechnique.fr NeXT - I don't have the name of the original author/porter of Scheme->C for the NeXT system. I must have lost the original posting to comp.lang.scheme. :-( However, David Broman (davbro@poincare.geom.umn.edu) and Scott S. Bertilson (scott@poincare.geom.umn.edu) tested my integration of the NeXT patches for the 28sep90 version. HP700 - osborne@merl.com (Randy Osborne) HP300 - eckelkamp@mcc.com (David Eckelkamp (me!)) RS/6000 - eckelkamp@mcc.com (David Eckelkamp (me!)) I am not able to verify the I386, PRISM, NeXT, APOLLO nor the Sony version of the system. I don't have access to those kinds of machines. The I386 patches seemed to mix conditionals between the 386 and SysV. It would be nice if someone with a 386 and SysV could figure out what things are SysV requirements. There are more SysV problems, though. You should look at the README that originally came with Critchlow's SPARC-I386 patches. The HP700 version appears to touch a bug in the C compiler I have access to. When compiling test02.sc in the test subdirectory this message is generated: cc: "test02.c", line 93: Internal error 4262: Investigation required \ - Please contact HP Support. The scheme compiler is able to compile and build executables from other test cases, though. The support for the RS/6000 is not complete. It compiles and runs many of the tests. It fails when trying to invoke a continuation that requires restoration of the stack (the continuation is not currently on the stack). It appears that the RS/6000 requires the stack to be valid before a longjmp can occur, whereas other machines do not. This problem can probably be circumvented, but it will require some assembly language routines and more knowledge than I possess on the internals of the RS/6000. The next version of Scheme->C may be easier to port to the RS/6000. I have not tried to integrate the AMIGA port that Mike Meyer (mwm@wse.dec.com) provided for the 01nov91 version of Scheme->C. Mostly it is because I forgot about that work, again. :-( Conclusion ========== I hope you find these changes useful. If you have any problems please let me know. I may not be able to respond immediately because my primary job does not include scheme work anymore; but I will try to respond. I appreciate any comments that you might have, good or bad. You can reach me via eckelkamp@mcc.com or uunet!cs.utexas.edu!milano!davide. ____________________________________________________________________________ David Eckelkamp Microelectronics and Computer Technology Corp. (MCC) eckelkamp@mcc.com 3500 W. Balcones Center Dr. (512) 343-0978 Austin,TX 78759 Another try at MCC02 *** /dev/null Mon Nov 30 15:30:41 1992 --- HP300 Mon Nov 30 10:21:34 1992 *************** *** 0 **** --- 1,33 ---- + # + # This is the header file for constructing make files for the HP 9000/300 + # + + SHELL=/bin/sh + + .SUFFIXES: + .SUFFIXES: .o .c .sc .s + + # Processor name: + + cpu = HP300 + + # Default flags to use when invoking the C compiler. + + CFLAGS = +O1 + CC = cc + CLIBS = -lm + + # Assembly language object files. + + Aruntime = hp300.o + + # Profiled library + + Plib = + + # Heap size in megabytes for the compiler. + + + scheapmb = 6 -scl 40 + + # End of HP900s300 header. *** /dev/null Mon Nov 30 15:30:42 1992 --- HP700 Mon Nov 30 13:48:08 1992 *************** *** 0 **** --- 1,33 ---- + # + # This is the header file for constructing make files for the HP 9000/700 + # + + SHELL=/bin/sh + + .SUFFIXES: + .SUFFIXES: .o .c .sc .s + + # Processor name: + + cpu = HP700 + + # Default flags to use when invoking the C compiler. + + CFLAGS = -O + CC = cc + CLIBS = -lm + + # Assembly language object files. + + Aruntime = hp700.o + + # Profiled library + + Plib = + + # Heap size in megabytes for the compiler. + + + scheapmb = 6 -scl 40 + + # End of HP700 (aka PARISC) header. *** /dev/null Mon Nov 30 15:30:44 1992 --- MCC-additions Mon Nov 30 10:21:35 1992 *************** *** 0 **** --- 1,244 ---- + + + Scheme->C Modification Summary + + + Opaque Objects + ============== + + Opaque objects were introduced in order to extend the type system to + accommodate C objects (structs) returned by the C interface system + (see below). The cdecl typing technique of a list whose car is the + type symbol was found to be too insecure. + + These new Scheme objects contain two pointers: a pointer to a symbol + (its type), and a pointer to a body (its value). One can read and + write these fields, and the collector can push through to their + values. An opaque object is created by: + + (make-opaque-object ) + + where is a symbol and is anything. The fields can be read and + written: + + (opaque-type ) + (set-opaque-type! ) + + (opaque-body ) + (set-opaque-body! ) + + and finally, there is the type predicate: + + (opaque? ) + + from which new secure type predicates can be formed: + + (define (foo? o) (and (opaque? o) (eq? (opaque-type o) 'foo))) + + Opaque objects can have a customized external representation (we + needed to write and read a C struct, for example). When an opaque + object is written, the *opaque-write-procedure* property of the type + symbol is examined to see if it has a procedure value. If so, that + procedure is applied to the opaque object, and returns a Scheme + expression representing the body. This expression is written within a + special syntax. For example, if a write-procedure evaluation for an + opaque object of type 'foo' results in '("hi" 42)', then this would be + written: + + #~(foo ("hi" 42)) + + The #~ syntax cues the reader to likewise look up an input conversion + function (the *opaque-read-procedure* property) which (if found) is + applied to an opaque-object containing the external representation. + This function should return a new body value to be inserted + into the object. The following code shows a real-life example: + + + ;;--------------------------------------------- pcsdesc-input-conversion + + (define (pcsdesc-input-conversion d) + (if (not (isa-pcsdescp? d)) + (error 'PCSDESC-INPUT-CONVERSION "argument no a PCS descriptor: ~S" d) + (let* ((body (opaque-body d)) + (host (check-arg (car body) string? "a string")) + (host-name-len (string-length host)) + (addr (check-arg (cadr body) string? "a string")) + (host-addr-len (string-length addr)) + (pid (check-arg (caddr body) integer? "a pid number")) + (port (check-arg (cadddr body) integer? "a port number")) + (tid (check-arg (cadr (cdddr body)) integer? "a tid number")) + (desc (make-pcsdesc)) ) + (do ((i 0 (+ i 1))) + ((>= i host-name-len)) + (pcsdesc-host! desc i (char->integer (string-ref host i))) ) + (do ((i 0 (+ i 1))) + ((>= i host-addr-len)) + (pcsdesc-addr! desc i (char->integer (string-ref addr i))) ) + (pcsdesc-pid! desc pid) + (pcsdesc-port! desc port) + (pcsdesc-tid! desc tid) + (opaque-body desc)))) + + (putprop 'pcsdescp '*opaque-read-procedure* pcsdesc-input-conversion) + + + ;;------------------------------------------- pcsdesc-output-conversion + + (define (pcsdesc-output-conversion d) + (let* ((desc (check-arg d isa-pcsdescp? "a PCS descriptor"))) + (list (pcsdesc-host-string desc) (pcsdesc-addr-string desc) + (pcsdesc-pid desc) (pcsdesc-port desc) (pcsdesc-tid desc)) )) + + (putprop 'pcsdescp '*opaque-write-procedure* pcsdesc-output-conversion) + + + An opaque object can also specify an evaluation function as the + *opaque-eval-procedure* property of its type symbol. This procedure is + passed the object, and returns the result of its evaluation. If no + such property is available, the opaque object is "self-evaluating". + + The opaque object changes touch object.h, object.c, scinit.c, heap.c, + scrt1.sc, predef.sc, scrt7.sc, and sceval.sc. + + + + C-interface + =========== + + The C-interface module is an adaptation of the cdecl system which + implements typedefs and externs as macros. This allows the c-interface + specifications to be intermixed with Scheme in a conventional Scheme + source file. This was seen as rather more convenient than the cdecl + approach, which pre-processes special-purpose files. The major problem + with cdecl, from our perspective, was that an addition or deletion of + a typedef or extern statement required a change to the system + construction (makefiles). We also added support for enum types. + + + + Catch(throw) + ============ + + A "catch" function has been added to callcc.c. This is derived from + the call/cc code, except that it does NOT save the stack (it DOES + save the display). The syntax is the same as call/cc: + + (catch (lambda (throw) + + (if + (throw ) + ...))) + + This is for implementing fast(er) exception handlers. If the throw + procedure is saved and invoked from out-of-stack (from another + continuation), an error is signalled. + + + + Object System + ============= + + A single-inheritance object system has been implemented entirely in + Scheme. It's design derives from several requirements which are + (perhaps) unique to our environment, but which may be shared by + others. To wit, small object size, small generated code, high + performance. The object system run-time support code MUST be + compiled in order to achieve high performance, but in compiled form, + it out-performs (compiled) object systems based on closures, even + for interactively defined objects. It uses *much* less data + space as well. + + + + System File Tasks and System Idle Tasks + ======================================= + + MCC modification to Scheme->C engine to enhance DEFINE-SYSTEM-FILE-TASK + support and to add "system idle task" support for additional event + processing that may not be explicitly associated with a file descriptor. + These enhancements were done in a manner that is fully backward-compatible + with the previous version of DEFINE-SYSTEM-FILE-TASK. + + Each System File Task may now be separately designated as "maskable" or + "non-maskable". This is used to designate whether the task may be + inhibited (masked) when SYSTEM-TASKING is turned off with + ENABLE-SYSTEM-FILE-TASKS, for example, when Scheme enters the debugger. + An optional final argument was added to DEFINE-SYSTEM- + FILE-TASK. (This change provides compatibity with any existing software + that uses DEFINE-SYSTEM-FILE-TASK). + + (DEFINE-SYSTEM-FILE-TASK []) + The designated tasks are associated with a system file number + by this procedure. The tasks are deleted by passing #F for + each task procedure. The optional arg, if #T, + designates the file task as nonmaskable. + + Also, SET-SYSTEM-FILE-TASK-MASKABLE was added to support changing the maskable + state of an existing system file task. + + (SET-SYSTEM-FILE-TASK-MASKABLE ) + Sets the maskable state of a system file task. + + In addition to System File Tasks, there are often cases where other operations + should be performed during the event loop, but the operations are not directly + associated with a UNIX system file descriptor. Therefore, DEFINE-SYSTEM-IDLE-TASK + was added for these cases. It is used to define tasks that are to be performed + just prior to the process system file tasks (and possibly blocking to wait for + further input through a call to select.) + + (DEFINE-SYSTEM-IDLE-TASK []) => + Defines a system idle task. DEFINE-SYSTEM-IDLE-TASK returns a "task + identifier" which is used as a "handle" for this task in other + operations. The idle task lambda/procedure must provide + a return value that indicates whether or not it has more processing + to do. #T indicates that it has further processing to do (but other + tasks are to be given a chance to execute). #F indicates that it has + no further processing to do at this time. + + (SET-SYSTEM-IDLE-TASK-MASKABLE ) + This is is used to change the maskable status of a designated task. + + (CANCEL-SYSTEM-IDLE-TASK ) + This is used to cancel a system idle task. + + Three functions were added for selective control of the event processing loop + (in WAIT-SYSTEM-FILE). On some occasions, it is undesirable to permit the + event loop to block, waiting for further input from a file descriptor, for + example when there are other pending operations that could take place in + a system FILE idle task or a system idle task. SYSTEM-TASK-BLOCK, SYSTEM-TASK- + NOBLOCK and SYSTEM-TASK-NOBLOCK-ON-SIGIO were added for assisting in dealing + with these less common event handling situations. + + (SYSTEM-TASK-BLOCK) + This is a function that requests the NEXT select call (within SYSTEM- + TASK-SELECT inside WAIT-SYSTEM-FILE) to block, waiting for input, if + nothing is available. + + (SYSTEM-TASK-NOBLOCK) + This is a function that requests the NEXT select call (within SYSTEM- + TASK-SELECT inside WAIT-SYSTEM-FILE) to NOT block, when no input is + available. + + (SYSTEM-TASK-NOBLOCK-ON-SIGIO) + This is a function that registers a SIGIO signal handler that causes + the NEXT select call (from within SYSTEM-TASK-SELECT inside WAIT- + SYSTEM-FILE) to be a non-blocking select whenever a SIGIO signal + occurs. The handler subsequently invokes the previously registered + (shadowed) SIGIO handler. + + These MCC system file task-related changes were made to be fully backwards + compatible with the previous DEFINE-SYSTEM-FILE-TASK and related functions + and args. + + EVENT PROCESSING: When there are no characters available on a port via + a File Descriptor (FD), the I/O system executes "system idle tasks" (not + associated with an FD) followed by "system file idle tasks" (associated + with an FD) and then followed by "system file tasks" (as a result of FD + activity) and then continues reading from the port FD when some read + completes. + (1) System idle tasks and system file tasks never interrupt an + executing Scheme program. + (2) System idle tasking and system file tasking are disabled while + in the debugger (with the exception of non-maskable tasks). + (3) All pending system file tasks are executed before continuing reads + from the port. *** /dev/null Mon Nov 30 15:30:48 1992 --- RS6000 Thu Feb 13 13:59:52 1992 *************** *** 0 **** --- 1,30 ---- + # + # This is the header file for constructing make files for IBM RS/6000 + # + + .SUFFIXES: + .SUFFIXES: .o .c .sc .s .u + + # Processor name: + + cpu = RS6000 + + # Default flags to use when invoking the C compiler. + + CFLAGS = -O + CC = cc + CLIBS = -lbsd -lm + + # Assembly language object files. + + Aruntime = + + # Profiled library + + Plib = + + # Heap size in megabytes for the compiler. + + scheapmb = 8 -scl 40 + + # End of RS6000 header. *** /dev/null Mon Nov 30 15:30:49 1992 --- SONYNEWS Thu Dec 12 15:18:33 1991 *************** *** 0 **** --- 1,30 ---- + # + # This is the header file for constructing make files for SONYNEWS processors. + # + + .SUFFIXES: + .SUFFIXES: .o .c .sc .s .u + + # Processor name: + + cpu = SONYNEWS + + # Default flags to use when invoking the C compiler. + + CFLAGS = -O2 -Olimit 1050 + CC = cc + CLIBS = -lm + + # Assembly language object files. + + Aruntime = mips.o + + # Profiled library + + Plib = + + # Heap size in megabytes for the compiler. + + scheapmb = 8 -scl 40 + + # End of SONYNEWS header. *** 1.3 1991/10/24 22:16:56 --- makefile 1992/11/30 16:21:36 *************** *** 2,9 **** # This file is used to make the Scheme->C system for multiple processor types. # #SRCDIR = /usrwrl/Gen/src/schemetoc ! SRCDIR = $${PWD:=`pwd`} MIPSDIR = /wrl/pmax/src/schemetoc MIPSBIN = /wrl/pmax/bin --- 2,11 ---- # This file is used to make the Scheme->C system for multiple processor types. # + SHELL=/bin/sh + #SRCDIR = /usrwrl/Gen/src/schemetoc ! SRCDIR = ${PWD} MIPSDIR = /wrl/pmax/src/schemetoc MIPSBIN = /wrl/pmax/bin *************** *** 41,49 **** NeXTBIN = /usr/local/bin NeXTLIB = /usr/local/lib # This is a list of the machines/architectures that are currently supported. # These are also the names of the necessary makefile fragements. ! MACHINES = APOLLO I386 ISC386IX MIPS NeXT PRISM SPARC SUN3 TITAN VAX # Architecture specific directories and links to the source files are # constructed by the following commands which follow: --- 43,68 ---- NeXTBIN = /usr/local/bin NeXTLIB = /usr/local/lib + SONYNEWSDIR = ${SRCDIR}/news_mips + SONYNEWSBIN = /usr/local/bin + SONYNEWSLIB = /usr/local/lib + + HP300DIR = ${SRCDIR}/hp300 + HP300BIN = ${SRCDIR}/bin.hp300 + HP300LIB = ${SRCDIR}/lib.hp300 + + HP700DIR = ${SRCDIR}/hp700 + HP700BIN = ${SRCDIR}/bin.hp700 + HP700LIB = ${SRCDIR}/lib.hp700 + + RS6kDIR = ${SRCDIR}/rs6000 + RS6kBIN = ${SRCDIR}/bin.rs6000 + RS6kLIB = ${SRCDIR}/lib.rs6000 + # This is a list of the machines/architectures that are currently supported. # These are also the names of the necessary makefile fragements. ! MACHINES = APOLLO HP300 HP700 I386 ISC386IX MIPS NeXT PRISM RS6000 SONYNEWS \ ! SPARC SUN3 TITAN VAX # Architecture specific directories and links to the source files are # constructed by the following commands which follow: *************** *** 161,166 **** --- 180,204 ---- forNeXT forNEXT: $(MAKE) "CPU = NeXT" "CPUDIR = ${NeXTDIR}" "SRCDIR = ${SRCDIR}" \ "BINDIR = ${NeXTBIN}" "LIBDIR = ${NeXTLIB}" forCPU + + forRS6000 forRS6k: + $(MAKE) "CPU = RS6000" CPUDIR=${RS6kDIR} "SRCDIR=${SRCDIR}" \ + "BINDIR=${RS6kBIN}" "LIBDIR=${RS6kLIB}" forCPU + + forSONYNEWS: + $(MAKE) "CPU = SONYNEWS" "CPUDIR = ${SONYNEWSDIR}" \ + "SRCDIR = ${SRCDIR}" "BINDIR = ${SONYNEWSBIN}" \ + "LIBDIR = ${SONYNEWSLIB}" forCPU + + forHP300 forHP9000s300: + $(MAKE) "CPU=HP300" "CPUDIR=${HP300DIR}" \ + "SRCDIR = ${SRCDIR}" "BINDIR=${HP300BIN}" \ + "LIBDIR=${HP300LIB}" forCPU + + forHP700 forHP9000s700: + $(MAKE) "CPU=HP700" "CPUDIR=${HP700DIR}" \ + "SRCDIR=${SRCDIR}" "BINDIR=${HP700BIN}" \ + "LIBDIR=${HP700LIB}" forCPU forI386-inplace: $(MAKE) "CPU = I386" "CPUDIR = /usr/local/src/24mar90" \ *** 1.2 1991/10/24 22:16:56 --- makefile-tail 1992/11/30 16:21:36 *************** *** 2,9 **** # This file is used to make the Scheme->C system for multiple processor types. # #SRCDIR = /usrwrl/Gen/src/schemetoc ! SRCDIR = $${PWD:=`pwd`} MIPSDIR = /wrl/pmax/src/schemetoc MIPSBIN = /wrl/pmax/bin --- 2,11 ---- # This file is used to make the Scheme->C system for multiple processor types. # + SHELL=/bin/sh + #SRCDIR = /usrwrl/Gen/src/schemetoc ! SRCDIR = $${PWD=`pwd`} MIPSDIR = /wrl/pmax/src/schemetoc MIPSBIN = /wrl/pmax/bin *************** *** 41,49 **** NeXTBIN = /usr/local/bin NeXTLIB = /usr/local/lib # This is a list of the machines/architectures that are currently supported. # These are also the names of the necessary makefile fragements. ! MACHINES = APOLLO I386 ISC386IX MIPS NeXT PRISM SPARC SUN3 TITAN VAX # Architecture specific directories and links to the source files are # constructed by the following commands which follow: --- 43,68 ---- NeXTBIN = /usr/local/bin NeXTLIB = /usr/local/lib + SONYNEWSDIR = ${SRCDIR}/news_mips + SONYNEWSBIN = /usr/local/bin + SONYNEWSLIB = /usr/local/lib + + HP300DIR = ${SRCDIR}/hp300 + HP300BIN = ${SRCDIR}/bin.hp300 + HP300LIB = ${SRCDIR}/lib.hp300 + + HP700DIR = ${SRCDIR}/hp700 + HP700BIN = ${SRCDIR}/bin.hp700 + HP700LIB = ${SRCDIR}/lib.hp700 + + RS6kDIR = ${SRCDIR}/rs6000 + RS6kBIN = ${SRCDIR}/bin.rs6000 + RS6kLIB = ${SRCDIR}/lib.rs6000 + # This is a list of the machines/architectures that are currently supported. # These are also the names of the necessary makefile fragements. ! MACHINES = APOLLO HP300 HP700 I386 ISC386IX MIPS NeXT PRISM RS6000 SONYNEWS \ ! SPARC SUN3 TITAN VAX # Architecture specific directories and links to the source files are # constructed by the following commands which follow: *************** *** 161,166 **** --- 180,204 ---- forNeXT forNEXT: $(MAKE) "CPU = NeXT" "CPUDIR = ${NeXTDIR}" "SRCDIR = ${SRCDIR}" \ "BINDIR = ${NeXTBIN}" "LIBDIR = ${NeXTLIB}" forCPU + + forRS6000 forRS6k: + $(MAKE) "CPU = RS6000" CPUDIR=${RS6kDIR} "SRCDIR=${SRCDIR}" \ + "BINDIR=${RS6kBIN}" "LIBDIR=${RS6kLIB}" forCPU + + forSONYNEWS: + $(MAKE) "CPU = SONYNEWS" "CPUDIR = ${SONYNEWSDIR}" \ + "SRCDIR = ${SRCDIR}" "BINDIR = ${SONYNEWSBIN}" \ + "LIBDIR = ${SONYNEWSLIB}" forCPU + + forHP300 forHP9000s300: + $(MAKE) "CPU=HP300" "CPUDIR=${HP300DIR}" \ + "SCRDIR = ${SRCDIR}" "BINDIR=${HP300BIN}" \ + "LIBDIR=${HP300LIB}" forCPU + + forHP700 forHP9000s700: + $(MAKE) "CPU=HP700" "CPUDIR=${HP700DIR}" \ + "SCRDIR = ${SRCDIR}" "BINDIR=${HP700BIN}" \ + "LIBDIR=${HP700LIB}" forCPU forI386-inplace: $(MAKE) "CPU = I386" "CPUDIR = /usr/local/src/24mar90" \ *** /dev/null Mon Nov 30 15:31:06 1992 --- contrib/mcc-c-interface-test.sc Wed Jan 8 07:14:23 1992 *************** *** 0 **** --- 1,56 ---- + + ;; this module implements access to some unix directory functions, and + ;; provides a directory procedure. + + (module mcc-c-interface-test) + + (include "mcc-c-interface.sc") + + (typedef (char 255) dir-name) + + (typedef (struct + (unsigned d-off) + (unsigned d-fileno) + (shortunsigned d-reclen) + (shortunsigned d-namlen) + (dir-name d-name)) + direct ) + + (typedef (direct *) directp) + + (make-typedef) ;; instantiates accessor functions for data types + + + ;;--------------------------------------------------- unix directory functions + + (extern unsigned "opendir" + (pointer dstream) ) + + (extern directp "readdir" + (pointer dstream) ) + + (extern void "closedir" + (pointer dstream) ) + + + ;;------------------------------------------------------------------ directory + (define (directory path) + + ;; return list of file names given a directory path. + + (if (not (string? path)) + (error 'directory "directory path is not a string: ~s" path) ) + (let ((d (opendir path))) + (if (zero? d) + '() + (let loop ((entry (readdir d)) + (names '()) ) + (if (zero? (opaque-body entry)) + names + (let* ((len (direct-d-namlen entry)) + (name (make-string len)) ) + (do ((i 0 (+ i 1))) + ((>= i len)) + (string-set! name i (integer->char (direct-d-name entry i))) ) + (loop (readdir d) (cons name names)))))))) + *** /dev/null Mon Nov 30 15:31:08 1992 --- contrib/mcc-c-interface.sc Fri Nov 20 15:01:08 1992 *************** *** 0 **** --- 1,1018 ---- + + ;;; C Declaration Language + + ;* Copyright 1989 Digital Equipment Corporation + ;* All Rights Reserved + ;* + ;* Permission to use, copy, and modify this software and its documentation is + ;* hereby granted only under the following terms and conditions. Both the + ;* above copyright notice and this permission notice must appear in all copies + ;* of the software, derivative works or modified versions, and any portions + ;* thereof, and both notices must appear in supporting documentation. + ;* + ;* Users of this software agree to the terms and conditions set forth herein, + ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free + ;* right and license under any changes, enhancements or extensions made to the + ;* core functions of the software, including but not limited to those affording + ;* compatibility with other hardware or software environments, but excluding + ;* applications which incorporate this software. Users further agree to use + ;* their best efforts to return to Digital any such changes, enhancements or + ;* extensions that they make and inform Digital of noteworthy uses of this + ;* software. Correspondence should be provided to Digital at: + ;* + ;* Director of Licensing + ;* Western Research Laboratory + ;* Digital Equipment Corporation + ;* 100 Hamilton Avenue + ;* Palo Alto, California 94301 + ;* + ;* This software may be distributed (but not offered for sale or transferred + ;* for compensation) to third parties, provided such third parties agree to + ;* abide by the terms and conditions of this notice. + ;* + ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + ;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + ;* SOFTWARE. + + ;; Now, to cover MCC's liability... + ;/* + ; * Copyright (c) 1991 + ; * Microelectronics and Computer Technology Corporation (MCC) + ; * All rights reserved. + ; * + ; * Use and copying of this software and preparation of derivative works + ; * based upon this software are permitted. However, any distribution of + ; * this software or derivative works must include the above copyright + ; * notice. + ; * + ; * This software is made available AS IS, and the MCC makes no + ; * warranty about the software, its performance or its conformity to + ; * any specification. + ; * + ; */ + + ;;; This module compiles type declarations. + ;;; + ;;; Data types are defined by this type of expression. Initially, we'll + ;;; try to accept as few forms as possible by doing a little "hand casting". + ;;; The legal forms are: + ;;; + ;;; (typedef ) + ;;; + ;;; where: + ;;; + ;;; ::= ( *) + ;;; ( *proc) + ;;; + ;;; ::= ( integer) + ;;; + ;;; + ;;; + ;;; ::= char + ;;; shortint + ;;; shortunsigned + ;;; int + ;;; unsigned + ;;; float + ;;; double + ;;; + ;;; + ;;; ::= denoting another type + ;;; + ;;; ::= ( struct [ ...] ) + ;;; ( union [ ...] ) + ;;; + ;;; ::= ( ) + ;;; + ;;; ::= ( enum [ ...] ) + ;;; + ;;; ::= | ( [0-9]+ ) + + + ;; *** Example of typedef and extern usage in a compiled source file: *** + + ;;======================================================================== + ;; this module implements access to some unix directory functions, and + ;; provides a directory procedure. + ; + ; (module dir-ops) + ; + ; (include "mcc-c-interface.sc") + ; + ; (typedef (char 255) dir-name) + ; + ; (typedef (struct + ; (unsigned d-off) + ; (unsigned d-fileno) + ; (shortunsigned d-reclen) + ; (shortunsigned d-namlen) + ; (dir-name d-name)) + ; direct ) + ; + ; (typedef (direct *) directp) + ; + ; (make-typedef) ;; instantiates accessor functions for data types + ; + ; + ; ;;--------------------------------------------------- unix directory functions + ; + ; (extern unsigned "opendir" + ; (pointer dstream) ) + ; + ; (extern directp "readdir" + ; (pointer dstream) ) + ; + ; (extern void "closedir" + ; (pointer dstream) ) + ; + ; + ; ;;------------------------------------------------------------------ directory + ; (define (directory path) + ; + ; ;; return list of file names given a directory path. + ; + ; (if (not (string? path)) + ; (error 'directory "directory path is not a string: ~s" path) ) + ; (let ((d (opendir path))) + ; (if (zero? d) + ; '() + ; (let loop ((entry (readdir d)) + ; (names '()) ) + ; (if (zero? (opaque-body entry)) + ; names + ; (let* ((len (direct-d-namlen entry)) + ; (name (make-string len)) ) + ; (do ((i 0 (+ i 1))) + ; ((>= i len)) + ; (string-set! name i (integer->char (direct-d-name entry i))) ) + ; (loop (readdir d) (cons name names)))))))) + ; + ; + ;;======================================================================== + + + ;; *** Example of enum usage: *** + + ;;======================================================================== + ; (module dir-ops) + ; + ; (include "mcc-c-interface.sc") + ; + ; (typedef (enum (ebadf 9) (epipe 29) (efbig 27) (efault 14)) write-return) + ; + ; (extern write-return "write" + ; (integer descriptor) (pointer buf) (integer nbytes)) + ; + ; (define unix-write (s d) + ; (let* ((len (string->length s)) + ; (status (write d s len)) ) + ; (if (= status len) + ; len + ; (error 'unix-write "error on write: ~a" + ; (enum->symbol write-return status) )))) + ; + ;;======================================================================== + + + + + ;;------------------------------ The following are all compile-time procedures + + (eval-when (compile eval) + + + (define *cdecl-types* '()) + (define *cdecl-procedures* '()) + + (define (push-procedure p) + (set! *cdecl-procedures* (cons p *cdecl-procedures*)) ) + + + ;;; Type declarations are parsed by the following function. It will return + ;;; the type definition, or call error on an error. Some of these type + ;;; transformations may be MACHINE DEPENDENT. + + (define (PARSE-TYPE type) + (if (pair? type) + (cond ((memq (car type) '(struct union)) + (struct-or-union type)) + ((eq? (car type) 'enum) + type) + ((equal? (cdr type) '(*)) + (list (parse-stype (car type)) '*)) + ((equal? (cdr type) '(*proc)) + (list (parse-stype (car type)) '*proc)) + (else (parse-atype type))) + (parse-stype type))) + + (define (PARSE-ATYPE type) + (if (pair? type) + (cond ((memq (car type) '(struct union)) + (struct-or-union type)) + ((and (= (length type) 2) + (integer? (cadr type)) (>= (cadr type) 0)) + (list (parse-stype (car type)) (cadr type))) + (else (error 'parse-atype "Argument is not a legal type: ~s" + type))) + (parse-stype type))) + + (define (PARSE-STYPE type) + (if (symbol? type) + type + (error 'parse-stype "Argument is not a legal type: ~s" type))) + + + ;;; Structs and unions are handled by the following functions. + + (define (STRUCT-OR-UNION exp) + (list (case (car exp) + ((struct) 'struct) + ((union) 'union) + (else (error 'struct-or-union "Illegal syntax: ~s" exp))) + (map (lambda (slot) + (if (and (= (length slot) 2) (symbol? (cadr slot))) + (list (parse-slot-type (car slot)) (cadr slot)) + (error 'struct-or-union + "Argument is not a legal slot: ~s" slot))) + (cdr exp)))) + + + ;;; When the type specifier for a slot is parsed, it may be contain an + ;;; array or structure definition, or a symbol. Arrays and structures + ;;; defined here must have a dummy type assigned to them. + + (define PARSE-SLOT-TYPE + (let ((uid 1)) + (lambda (type) + (let ((parse (parse-atype type))) + (if (symbol? parse) + parse + (let ((symbol + (string->symbol (format "*TYPE~s" uid)))) + (set! uid (+ uid 1)) + (putprop symbol '*cdecl-base-type* #f) + (putprop symbol '*cdecl-type* parse) + symbol)))))) + + + (define (identity x) x) + + ;;; The base types recognized by the type system are known to C and have + ;;; known bytes sizes WHICH MAY BE MACHINE DEPENDENT. + + (for-each + (lambda (x) + (let ((type (list-ref x 0)) + (size (list-ref x 1)) + (to-ref (list-ref x 2)) + (to-set! (list-ref x 3))) + (putprop type '*cdecl-type* #t) + (putprop type '*cdecl-base-type* type) + (putprop type '*cdecl-size* size) + (putprop type '*cdecl-to-get* to-ref) + (putprop type '*cdecl-to-set!* to-set!))) + + ; C type size to access to set! + + '((char 1 c-byte-ref c-byte-set!) + (shortint 2 c-shortint-ref c-shortint-set!) + (shortunsigned 2 c-shortunsigned-ref c-shortunsigned-set!) + (int 4 c-int-ref c-int-set!) + (unsigned 4 c-unsigned-ref c-unsigned-set!) + (pointer 4 c-unsigned-ref c-unsigned-set!) + (procedure 4 c-unsigned-ref c-unsigned-set!) + (float 4 c-float-ref c-float-set!) + (double 8 c-double-ref c-double-set!) + (tscp 4 c-tscp-ref c-tscp-set!))) + + + ;;; Every type symbol can be resolved into a base type symbol by the following + ;;; function. Once a base type has been computed, it is saved on the + ;;; property list. + + (define (BASE-TYPE start-type) + (or (getprop start-type '*cdecl-base-type*) + (let loop ((type start-type) (count 20)) + (let ((typeinfo (getprop type '*cdecl-type*))) + (if (or (not typeinfo) (eq? count 0)) + (error 'base-type "BASE TYPE cannot be resolved: ~s" + start-type)) + (if (symbol? typeinfo) + (loop typeinfo (- count 1)) + (putprop start-type '*cdecl-base-type* type)))))) + + + ;;; Basic information about a type is returned by: + + (define (ISA-UNION? type) + (let ((typeinfo (getprop (base-type type) '*cdecl-type*))) + (and (pair? typeinfo) (eq? (car typeinfo) 'union)))) + + (define (ISA-STRUCT? type) + (let ((typeinfo (getprop (base-type type) '*cdecl-type*))) + (and (pair? typeinfo) (eq? (car typeinfo) 'struct)))) + + (define (UORS-SLOTS type) (cadr (getprop (base-type type) '*cdecl-type*))) + + (define (ISA-PROCP? type) + (let ((typeinfo (getprop (base-type type) '*cdecl-type*))) + (and (pair? typeinfo) (eq? (cadr typeinfo) '*proc)))) + + (define (PROCP-RETURNS type) + (base-type (car (getprop (base-type type) '*cdecl-type*)))) + + (define (ISA-POINTER? type) + (let ((typeinfo (getprop (base-type type) '*cdecl-type*))) + (and (pair? typeinfo) (eq? (cadr typeinfo) '*)))) + + (define (POINTER-TO type) + (base-type (car (getprop (base-type type) '*cdecl-type*)))) + + (define (ISA-ARRAY? type) + (let ((typeinfo (getprop (base-type type) '*cdecl-type*))) + (and (pair? typeinfo) (number? (cadr typeinfo))))) + + (define (ARRAY-SIZE type) (cadr (getprop (base-type type) '*cdecl-type*))) + + (define (ARRAY-TYPE type) (base-type (car (getprop (base-type type) '*cdecl-type*)))) + + (define (POINTED-TO-BY type) + (base-type (getprop (base-type type) 'pointed-to-by))) + + + ;;; enum "stuff" + + (define (EMIT-ENUM-FORMS type) + (when (isa-enum? type) + (for-each + (lambda (var) + (push-procedure `(define-enum ,var ,type))) + (reverse (cdr (getprop type '*cdecl-type*)))) + ;; push this on the list *last* so it will actually be first! + (push-procedure `(define ,type '())))) + + (define (ISA-ENUM? type) + (let ((cdecl-type (getprop type '*cdecl-type*))) + (and (pair? cdecl-type) (eq? (car cdecl-type)'enum)))) + + (define (CANONICALIZE-ENUM type type-list accessor) + (and (string? type) (set! type (string->symbol type))) + (cond + ((integer? type) + (let ((val (assq type type-list))) + (when val + (accessor val)))) + ((symbol? type) + (do ((rem type-list (cdr rem)) + (match? #f)) + ((or match? (not rem)) + (when match? + (accessor match?))) + (when (eq? type (cdar rem)) + (set! match? (car rem))))) + (else + #f))) + + ;;; + ;;; This function (ENUM->SYMBOL) is especially useful for + ;;; comprehending an ENUM return value. + ;;; + (define (ENUM->SYMBOL type-list type) + (canonicalize-enum type type-list cdr)) + + (define (ENUM->INT type-list type) + (canonicalize-enum type type-list car)) + + (define (CHK-ENUM type-list type-list-symbol type) + (or (enum->int type-list type) + (error 'chk-enum "Argument is invalid ~s enumeration: ~s" + type type-list-symbol))) + + (define (next-enum type-list) + (if type-list (+ (caar type-list) 1) 0)) + + + + ;;; Given this information, we can now compute sizes of things. There may + ;;; be future MACHINE DEPENDENT problems here as we aren't worrying about + ;;; alignment. + + (define (SIZE-OF type) + + (define (SIZE-OF-SU slots func) + (let ((size 0)) + (for-each + (lambda (slot) + (set! size (func size (size-of (car slot))))) + slots) + size)) + + (cond ((getprop (base-type type) '*cdecl-size*)) + ((isa-enum? type) (size-of 'int)) + ((isa-union? type) (size-of-su (uors-slots type) max)) + ((isa-struct? type) (size-of-su (uors-slots type) +)) + ((isa-procp? type) (size-of 'procedure)) + ((isa-pointer? type) (size-of 'pointer)) + ((isa-array? type) (* (array-size type) (size-of (array-type type)))) + (else (error 'size-of "Mystery type: ~s" type)))) + + + ;;; A method for loading a type which takes an object, an offset, and an + ;;; index (only for arrays) as it's arguments is returned by the following + ;;; function. + + (define (TO-GET-TYPE type) + (let ((base (base-type type))) + (cond ((getprop base '*cdecl-to-get*)) + ((isa-enum? base) + `(lambda (x y) + (chk-enum ,base ',base (,(to-get-type 'int) x y)))) + ((isa-array? base) + `(lambda (x y i) + (,(to-get-type (array-type base)) x + (+ y (* ,(size-of (array-type base)) i))))) + ((isa-pointer? base) + `(lambda (x y) + (make-opaque-object ',base + (,(to-get-type 'pointer) x y)))) + ((isa-procp? base) + `(lambda (x y) + (make-opaque-object ',base + (,(to-get-type 'pointer) x y)))) + (else #f)))) + + + ;;; A method for storing a type which takes an object, an offset, an index + ;;; (only for arrays), and a new value as it's arguments is returned by the + ;;; following function. + + (define (TO-SET!-TYPE type) + (let ((base (base-type type))) + (cond ((getprop base '*cdecl-to-set!*)) + ((isa-enum? base) + `(lambda (x y z) + (,(to-set!-type 'int) x y (chk-enum ,base ',base z)))) + ((isa-array? base) + `(lambda (x y i z) + (,(to-set!-type (array-type base)) x + (+ y (* ,(size-of (array-type base)) i)) + ;;; this is a bug!: (,(to-check-type (array-type base)) z)))) + ;;; it should be this: (I think) Kevin + z))) + ((isa-pointer? base) + `(lambda (x y z) + (,(to-set!-type 'pointer) x y + (,(to-check-type base) z)))) + ((isa-procp? base) + `(lambda (x y z) + (,(to-set!-type 'pointer) x y + (,(to-check-type base) z)))) + (else #f)))) + + + ;;; A method for checking a type and returning the "raw" value which takes an + ;;; object as it's argument is returned by the following function. + + (define (TO-CHECK-TYPE type) + (let ((base (base-type type))) + (if (or (isa-pointer? base) (isa-procp? base)) + (uis "CHK-" base) + '(lambda (x) x)))) + + + ;;; The symbol that is used as the type tag for objects is returned by the + ;;; following procedure. It returns #f when there is no type tag. + + (define (TYPE-TAG type) + (let ((base (base-type type))) + (if (or (isa-pointer? base) (isa-procp? base)) + base + #f))) + + + ;;; Converts a list of strings or symbols into an upper-case interned symbol. + + (define (UIS . syms) + (string->symbol + (list->string + (let loop ((syms syms)) + (if syms + (append (map char-upcase + (string->list + (if (symbol? (car syms)) + (symbol->string (car syms)) + (car syms)))) + (loop (cdr syms))) + '()))))) + + + ;;; Scheme code for type definitions is emitted by the following procedure + ;;; which is called with a list of all type names, a list of definition + ;;; only types, and a list of read-only types, and the filename/modulename + ;;; prefix. + + (define (EMIT-TYPEDEFS types define-only read-only) + (define (EMIT-TYPE type read-only) + (cond ((isa-enum? type) + (emit-enum-forms type)) + ((isa-pointer? type) + (emit-chk-procs type def-print) + (cond ((or (isa-union? (pointer-to type)) + (isa-struct? (pointer-to type))) + (emit-struct-procs type read-only) ) + ((isa-array? (pointer-to type)) + (emit-array-procs type read-only def-print)))) + ((isa-procp? type) + (emit-chk-procs type def-print)))) + + (define (DEF-PRINT exp) + (format "defining ~S~%" exp) + (eval exp) ) + + (for-each + (lambda (type) + (unless (or (memq type define-only) + (not (eq? type (base-type type)))) + (emit-type type (memq type read-only)))) + types)) + + + ;;; Checking functions for procedure pointer types are emitted by the + ;;; following procedure. The arguments are the object type and the procedure + ;;; to print the definitions. + + (define (EMIT-CHK-PROCS type def-print) + (push-procedure `(define (,(uis "CHK-" (type-tag type)) x) + (if (and (opaque? x) + (eq? (opaque-type x) ',(type-tag type))) + (opaque-body x) + (error ',(uis "CHK-" (type-tag type)) + "Argument is incorrect type: ~s" x)))) + (push-procedure `(define (,(uis "ISA-" (type-tag type) "?") x) + (and (opaque? x) + (eq? (opaque-type x) ',(type-tag type)))))) + + + + ;;; Access functions for array types are generated by the following procedure. + ;;; The arguments are the object type, a read-only flag, and the function to + ;;; print the definitions. + + (define (EMIT-ARRAY-PROCS pointer read-only def-print) + (let* ((type (pointer-to pointer)) + (size (array-size type)) + (entry-type (array-type type)) + (chk (to-check-type pointer))) + (push-procedure `(define (,(uis type "-LENGTH") x) + (quotient (string-length (,chk x)) + ,(size-of entry-type)))) + (cond ((or (isa-struct? entry-type) (isa-union? entry-type)) + (push-procedure + `(define (,(uis type "->" entry-type "-LIST") x) + (let* ((array (,chk x)) + (asize (string-length array)) + (esize ,(size-of entry-type))) + (let loop ((x 0)) + (if (eq? x asize) + '() + (cons (make-opaque-object + ',(pointed-to-by entry-type) + (substring array x (+ x esize))) + (loop (+ x esize)))))))) + (push-procedure + `(define (,(uis entry-type "-LIST->" type) x) + (make-opaque-object ',pointer + (apply string-append + (map ,(to-check-type + (pointed-to-by + entry-type)) + x)))))) + (else + (push-procedure + `(define (,type x i) + (,(to-get-type type) (,chk x) 0 i))) + (push-procedure + `(define (,(uis type "->" entry-type "-LIST") x) + (let loop ((i 0) + (count (,(uis type "-LENGTH") x))) + (if (eq? i count) + '() + (cons (,type x i) + (loop (+ i 1) count)))))) + (push-procedure + `(define (,(uis entry-type "-LIST->" type) l) + (let loop ((l l) + (i 0) + (a (,(uis "MAKE-" type) + ,@(if (eq? size 0) + '((length l)) + '())))) + (if l + (begin (,(uis type "!") a i (car l)) + (loop (cdr l) (+ i 1) a)) + a)))) + (push-procedure + `(define (,(uis type "!") x i z) + (,(to-set!-type type) (,chk x) 0 i z))) + (push-procedure + `(define (,(uis "MAKE-" type) + ,@(if (eq? size 0) '(x) '())) + (make-opaque-object ',pointer + (make-string + (* ,(size-of entry-type) + ,(if (eq? size 0) 'x size)) + (integer->char 0))))))))) + + + ;;; Create struct definition functions. + + (define (EMIT-STRUCT-PROCS pointer read-only ) + (let* ((type (pointer-to pointer)) + (slots (uors-slots type))) + (if slots + (let* () + (define (DEF-PRINT exp) + (format "defining ~S~%" exp) + (eval exp) ) + + (push-procedure + `(define (,(uis "MAKE-" type)) + (make-opaque-object ',pointer + (make-string + ,(* (quotient + (+ (size-of type) 3) + 4) + 4) + (integer->char 0))))) + (slot-getset type type 0 pointer read-only def-print) )))) + + + ;;; Slot access functions for a structure are created by the following + ;;; function. + + (define (SLOT-GETSET type preamble offset base-type read-only def-print) + + (define (EMIT-PROCS type name offset) + (let ((index (if (isa-array? type) '(i) '()))) + (push-procedure + `(define (,(uis preamble "-" name) x ,@index) + (,(to-get-type type) + (,(to-check-type base-type) x) + ,offset + ,@index))) + (unless read-only + (push-procedure + `(define + (,(uis preamble "-" name "!") x ,@index y) + (,(to-set!-type type) + (,(to-check-type base-type) x) + ,offset + ,@index + y)))))) + + (let loop ((slots (uors-slots type)) (offset offset)) + (if slots + (let* ((slot-type (caar slots)) + (slot-name (cadar slots))) + (cond ((or (isa-union? slot-type) (isa-struct? slot-type)) + (slot-getset slot-type + (uis preamble "-" slot-name) + offset base-type read-only def-print)) + (else (emit-procs slot-type slot-name offset))) + (loop (cdr slots) + (if (isa-union? type) + offset + (+ offset (size-of slot-type)))))))) + + + + + ;;-------------------------------------------------------------------- Externs + + ;;; This module compiles "extern" forms which define C library procedures. + ;;; + ;;; ::= ( EXTERN [ ... ] ) + ;;; + ;;; ::= a Scheme string + ;;; + ;;; ::= ( ) + ;;; ( IN ) + ;;; ( OUT ) + ;;; ( IN_OUT ) + ;;; + ;;; ::= a Scheme symbol + + + (define *cdecl-externs* '()) + + (define (push-extern p) + (set! *cdecl-externs* (cons p *cdecl-externs*)) ) + + + ;;; Parses the argument list and calls error on an error. + + (define (PARSE-ARG exp) + (if (and (pair? exp) + (or (and (= (length exp) 2) + (parse-type (car exp)) + (symbol? (cadr exp))) + (and (= (length exp) 3) + (memq (car exp) '(in out in_out)) + (parse-type (cadr exp)) + (symbol? (caddr exp))))) + #t + (error 'PARSE-ARG "Illegal ARGUMENT syntax: ~s" exp))) + + + ;;; The definition for the interface procedure for an extern is created by + ;;; the following procedure. + + (define (EMIT-EXTERNAL extern defform) + (let ((scheme-proc (if (pair? (caddr extern)) + (car (caddr extern)) + (uis (caddr extern)) )) + (c-external (if (pair? (caddr extern)) + (cadr (caddr extern)) + (caddr extern) )) + (c-proc (if (pair? (caddr extern)) + (uis (car (caddr extern)) "*") + (uis (caddr extern) "*") )) + + (rettype (cadr extern)) + (args (cdddr extern))) + + (define (EMIT-CALL) + `(,c-proc ,@(map (lambda (x) (car (last-pair x))) args))) + + (define (FORMALS args) + (if args + (if (eq? (caar args) 'out) + (formals (cdr args)) + (cons (car (last-pair (car args))) + (formals (cdr args)))) + '())) + + (push-extern `(define-c-external + (,c-proc ,@(map simple-type args)) + ,(simple-type (list rettype 'returned)) + ,c-external)) + (newline) + (push-extern `(,defform (,scheme-proc ,@(formals args)) + (let* (,@(map arg-in args) + (return-value + ,(cond ((eq? rettype 'void) + `(begin ,(emit-call) #f)) + ((eq? rettype 'string) + `(c-string->string ,(emit-call))) + ((eq? rettype 'boolean) + `(not (= ,(emit-call) 0))) + ((isa-enum? rettype) + `(chk-enum ,rettype ',rettype ,(emit-call))) + ((isa-pointer? rettype) + `(make-opaque-object ',(base-type rettype) + ,(emit-call))) + (else (emit-call))))) + ,(let ((out (args-out args))) + (if out + (if (eq? rettype 'void) + (if (= (length out) 1) + (car out) + `(list ,@out)) + `(list return-value ,@out)) + 'return-value))))) + (newline))) + + ;;; Called to do input conversion for arguments. Return an expression + ;;; of th form ( ). + + (define (ARG-IN arg) + (let* ((flag (if (memq (car arg) '(in out in_out)) + (car arg) + #f)) + (type (if flag (cadr arg) (car arg))) + (var (if flag (caddr arg) (cadr arg)))) + (case flag + ((in) `(,var (in->c ,var))) + ((in_out) `(,var (in_out->c ,var))) + ((out) `(,var (make-string ,(if (eq? type 'string) + 4 + (size-of type))))) + (else (cond ((eq? type 'string) + `(,var (if (string? ,var) + ,var + (error 'chk-string + "Argument is incorrect type: ~s" + ,var)))) + ((eq? type 'boolean) + `(,var (if ,var 1 0))) + ((isa-enum? type) + `(,var (chk-enum ,type ',type ,var))) + ((isa-pointer? type) + `(,var (,(uis "CHK-" (base-type type)) ,var))) + (else `(,var ,var))))))) + + ;;; Return a list of the expressions required to do output conversion after + ;;; an external call. + + (define (ARGS-OUT args) + + (define (ARG-OUT arg) + (let* ((flag (if (memq (car arg) '(in out in_out)) + (car arg) + #f)) + (type (if flag (cadr arg) (car arg))) + (var (if flag (caddr arg) (cadr arg)))) + (case flag + ((in) #f) + ((in_out) `(c->in_out ,var)) + ((out) + (cond ((eq? type 'string) + `(c-string->string (c-unsigned-ref ,var 0))) + ((isa-enum? type) + `(chk-enum ,type ',type (c-int-ref ,var 0))) + ((isa-pointer? type) + `(make-opaque-object ',(base-type type) + (c-unsigned-ref ,var 0))) + ((or (isa-union? type) (isa-struct? type) + (isa-array? type)) + `(make-opaque-object ',(pointed-to-by type) + ,var)) + (else `(,(getprop (base-type type) '*cdecl-to-get*) + ,var 0)))) + (else #f)))) + + (if args + (let ((out (arg-out (car args)))) + (if out + (cons out (args-out (cdr args))) + (args-out (cdr args)))) + '())) + + ;;; Converts the type of a procedure argument to a simple C-type. + + (define (SIMPLE-TYPE type) + (cond ((memq (car type) '(in out in_out string)) 'pointer) + ((eq? (car type) 'boolean) 'int) + ((eq? (car type) 'void) 'void) + ((isa-enum? (car type)) 'int) + ((isa-pointer? (car type)) 'pointer) + ((isa-procp? (car type)) 'pointer) + (else (base-type (car type))))) + + + ;; *** Warning, all this stub file stuff is not yet converted!!! + + ;;; The STUBS file is written by the following function. + + (define (EMIT-STUBS externs stubs-file-root) + (with-output-to-file + (string-append stubs-file-root ".sc") + (lambda () + (write `(module ,(uis stubs-file-root))) + (newline) + (for-each emit-stub externs)))) + + ;;; The external definition for a procedure is written by the following + ;;; function. + + (define (EMIT-DEFINE-EXTERNAL extern module) + (let ((formals (let loop ((args (cdddr extern)) + (formals '(a b c d e f g h i j k l m + n o p q r s t u v w x y z))) + (cond ((null? args) '()) + ((eq? (caar args) 'out) + (loop (cdr args) (cdr formals))) + (else (cons (car formals) + (loop (cdr args) (cdr formals)))))))) + + (push-extern `(define-external (,(uis (caddr extern)) ,@formals) ,module)) + (newline))) + + ;;; The definition for a stub procedure is written by the following function. + + (define (EMIT-STUB extern) + (let* ((c-name (uis (caddr extern) "**")) + (stub-name (uis (caddr extern) "*")) + (rettype (cadr extern)) + (args (cdddr extern)) + (formals (let loop ((args args) + (formals '(a b c d e f g h i j k l m + n o p q r s t u v w x y z))) + (if (not (null? args)) + (cons (car formals) + (loop (cdr args) (cdr formals))) + '())))) + + (push-extern `(define-c-external + (,c-name ,@(map simple-type args)) + ,(simple-type (list rettype 'returned)) + ,(caddr extern))) + (newline) + (push-extern `(define (,stub-name ,@formals) + (,c-name ,@formals) + ,@(if (eq? rettype 'void) '(#f) '()))) + (newline))) + + + );; end eval-when + + + ;====================================================================== macros + + + ;;---------------------------------------------------------------- define-enum + ;;; + ;;; DEFINE-ENUM is a utility function that is used for defining + ;;; variables that are additionally maintained in a specified alist. + ;;; (The ENUM canonicalization functions, ENUM->SYMBOL and ENUM->INT + ;;; functions (above) may be used to canonicalize a symbol, string, or + ;;; value (typically integer) reference into a symbol or integer + ;;; representation, respectively. The ENUM->SYMBOL function is + ;;; especially useful for comprehending an ENUM return value.) + ;;; + (define-macro DEFINE-ENUM + (lambda (form expander) + (expander + (let* ((variable-name (cadr form)) + (list-name (caddr form)) + (variable-value (if (pair? variable-name) + (let ((val (cadr variable-name))) + (set! variable-name (car variable-name)) + val) + ;; otherwise, find a "suitable" value + `(next-enum ,list-name)))) + `(define ,variable-name + (let ((initial-value ,variable-value)) + (set! ,list-name (cons (cons initial-value ',variable-name) + ,list-name)) + initial-value))) + expander))) + + ;;-------------------------------------------------------------------- typedef + + (define-macro + TYPEDEF + (lambda (exp e) + (if (and (= (length exp) 3) (symbol? (caddr exp))) + (let ((id (caddr exp)) + (parse (parse-type (cadr exp)))) + (putprop id '*cdecl-base-type* #f) + (putprop id '*cdecl-type* parse) + (if (and (pair? parse) (symbol? (car parse)) + (eq? (cadr parse) '*)) + (putprop (car parse) 'pointed-to-by id)) + (set! *cdecl-types* (cons id *cdecl-types*)) + #t ) + (error 'typedef "Illegal syntax: ~s" exp)))) + + + ;;------------------------------------------------------------- clear-typedefs + + (define-macro + CLEAR-TYPEDEFS + (lambda (exp e) + (set! *cdecl-types* '()) + (set! *cdecl-procedures* '()) + #t)) + + + + ;;--------------------------------------------------------------- make-typedef + + (define-macro + MAKE-TYPEDEF + (lambda (exp e) + (let ((id-list (if (> (length exp) 1) + (cdr exp) + (reverse *cdecl-types*) ))) + (set! *cdecl-procedures* '()) + (emit-typedefs id-list '() '()) + (e `(begin ,@*cdecl-procedures* #t) + e )))) + + + ;;--------------------------------------------------------------------- extern + + (define-macro + EXTERN + (lambda (exp e) + (if (and (>= (length exp) 3) + (parse-type (cadr exp)) + (or (and (pair? (caddr exp)) + (symbol? (car (caddr exp))) + (string? (cadr (caddr exp))) ) + (string? (caddr exp)) )) + (begin + (set! *cdecl-externs* '()) + (if (> (length exp) 3) + (for-each parse-arg (cdddr exp))) + (emit-external exp 'define) + (e `(begin ,@(reverse *cdecl-externs*) #t) + e )) + (error 'extern "Illegal EXTERN syntax: ~s" exp)))) + *** /dev/null Mon Nov 30 15:31:10 1992 --- contrib/mcc-sos-test.sc Fri Nov 20 15:01:56 1992 *************** *** 0 **** --- 1,87 ---- + + (module mcc-sos-test) + + (include "mcc-sos.sch") + + ;;------------------------------------------------------------------ my-vector + + (declare-class my-vector + + (class-data (vectors '())) + + (data + (v #f) + (sz 0) ) + + (methods + (vsize () sz) + (vref (i) (vector-ref v i)) + (vset! (i val) (vector-set! v i val)) ) + + (constructor + (size) () + (cond ((> size 0) + (set! v (make-vector size)) + (set! sz size) + (set! vectors (cons v vectors)) ) + (else + (error 'make-vector "Size must be positive") ))) + + (destructor + () () + (set! sz 0) + (set! v #f) ) + ) + + ;;--------------------------------------------------------------------- my-vec + + (declare-class my-vec + + (parent my-vector) + + (data + (hi 0 my-vec-hi-index) + (lo 0 my-vec-lo-index) ) + + (methods + (check-range (i) (if (or (< i lo) (> i hi) ) + (error 'my-vec "index out of range") + (- i lo) )) + (show-vectors () vectors) + (vref (i) (vector-ref v (send self check-range i))) + (vset! (i val) (vector-set! v (send self check-range i) val)) ) + + (constructor + (low high) + ((+ 1 (abs (- high low)))) ;calculate size for parent + (set! lo (min low high)) + (set! hi (max low high)) ) + ) + + (declare-method show-all my-vec () (list vectors sz v)) + + ;; instantiate classes + (define-class my-vec my-vector) + + + (define (test-sos) + ;;----------------------------------------------------------- test constuctors + (list + (set! y (make-my-vector 10)) + (set! x (make-my-vec 4 10)) + + + ;;---------------------------------------------------------- test method calls + (send y vset! 4 "hi") + (send y vref 4) + (send x vset! 4 "hi") + (send x vref 4) + (send x my-vec-hi-index) + (send x my-vec-lo-index) + + + ;;------------------------------------ test declare-method and class variables + (send x show-all) + (send x show-vectors) + )) + *** /dev/null Mon Nov 30 15:31:11 1992 --- contrib/mcc-sos.doc Fri Nov 20 15:01:58 1992 *************** *** 0 **** --- 1,411 ---- + ;/* + ; * Copyright (c) 1991 + ; * Microelectronics and Computer Technology Corporation (MCC) + ; * All rights reserved. + ; * + ; * Use and copying of this software and preparation of derivative works + ; * based upon this software are permitted. However, any distribution of + ; * this software or derivative works must include the above copyright + ; * notice. + ; * + ; * This software is made available AS IS, and the MCC makes no + ; * warranty about the software, its performance or its conformity to + ; * any specification. + ; * + ; */ + + + MCC SOS - Scheme Object System + ========================== + + The Scheme Object System is a fairly conventional (!) single-inheritance + object system. It uses data slots, methods, constructors, destructors, and + accessors. + + The syntax of a class declaration is: + + (declare-class + [(parent )] + [(data ( []) ... )] + [(class-data ( []) ... )] + [(methods ( ( ...) ) ...)] + [(constructor ( ...) ( ...) )] + [(destructor ( ...) ( ...) )] + ) + + In the data or class-data slots, may be a symbol like + 'foo', in which case the reader is called 'foo' and the writer 'foo!'. + Keywords can also be used to specify seperate reader and writer + functions: + + ... (data (x 0 reader get-x writer put-x) + (y 0 reader get-y) ) ... + + In this case, no writer function is generated for y. + + The sections of the declaration can be in any order, and all are + optional (although at least ONE must appear). This is a DECLARATION, + not a definition. That is, you can put a bunch of these declarations + in an "include" file just like C declarations. A class is DEFINED by: + + (define-class ...) + + This actually generates code for the class operations. Classes may be + DEFINED in any order, provided they and all of their parent classes + have been DECLARED. The following examples shall be used to + illustrate: + + (declare-class my-vector + (class-data + (vectors '())) + (data (v #f) + (sz 0) ) + (methods (vsize () sz) + (vref (i) (vector-ref v i)) + (vset! (i val) (vector-set! v i val)) ) + (constructor (size) () + (cond ((> size 0) + (set! v (make-vector size)) + (set! vectors (cons v vectors)) + (set! sz size) ) + (else + (error 'make-vector "Size must be positive") ))) + (destructor () () + (set! sz 0) + (set! v #f) ) + ) + + (define-class my-vector) + + Now, you would use such a class like: + + >(set! x (make-my-vector 10)) + #(1 #(() () () () () () () () () ()) 10) + >(send x vsize) + 10 + >(send x vset! 9 "hi") + "hi" + >(send x vref 9) + "hi" + + Ok so far? Alright, now we'll derive a vector class with specifiable index + limits, such as a vector indexed 5 to 10, for example. + + (declare-class my-vec + (parent my-vector) + (data (hi 0 my-vec-hi-index) + (lo 0 my-vec-lo-index) ) + (methods (check-range (i) (if (or (< i lo) (> i hi) ) + (error 'my-vec "index out of range") + (- i lo) )) + (vref (i) (vector-ref v (send self check-range i))) + (vset! (i val) (vector-set! v (send self check-range i) val)) ) + (constructor (low high) + ((+ 1 (abs (- high low)))) ;calculate size for parent + (set! lo (min low high)) + (set! hi (max low high)) ) + ) + + (define-class my-vec) + + Now, we've derived a new class from my-vector, and added the HI and LO + data slots. Unlike the parent class data slots, these slots have + accessors which allow their direct access by non-methods. For example, + if X is a MY-VEC object, the HI slot can be read by: + + (send x my-vec-hi-index) + + and written by: + + (send x my-vec-hi-index! 3) + + (Of course, this is not a good idea in this particular case!) One can + also specify reader and writer function using the reader/writer + keywords. Now, we also added a method CHECK-RANGE, and re-defined VREF + and VSET! to check the range of the specified index. We had the + MY-VEC constructor calculate and pass the vector size to the + constructor of the parent class (who actually allocates the vector + space), and the constructor also sets the HI and LO fields. We would + use it like: + + (set! x (make-my-vec 5 10)) + #(2 #(() () () () () ()) 6 10 5) + > (send x vset! 11 "hi") + ***** MY-VEC index out of range + (ERROR 'MY-VEC "index out of range") in ENV-0 + (VECTOR-SET! (VECTOR-REF SELF 1) ((SOS-GENERIC-DISPATCHER CHECK-R ... in ENV-1 + (EVAL ...) + (SCREP_REP ...) + (READ-EVAL-PRINT ...) + >> + > (send x vset! 10 "hi") + "hi" + > (send x vref 10) + "hi" + > + + Classes can be re-defined on the fly. If data slots change, all + derived class types must also be re-defined and object instances + re-made. However, methods can be changed or added without + disturbing the rest of the definitions OR existing object instances. + This should provide fairly convenient interactive class development. + + An inherited method can be invoked from within a method by: + + (inherited-method foo 2 3 4) + + This guarantees that the selected foo method will come from a parent + class. + + Let's define a new method for my-vector which shows us the value of + the 'vectors' class variable: + + (declare-method show-vectors my-vec () vectors) + + (define-class my-vector) + + Now we can say (using the existing object x!) + + (send x show-vectors) + + and get: + + (#(() () () () () () () () () "hi") #(() () () () () "hi")) + + The DECLARE-METHOD macro is considered an addendum to the class + declaration, and so must appear after the corresponding DECLARE-CLASS + macro. + + Class variables get re-initilized every time the class is re-defined. + If we had put the 'show-vectors' method on the 'my-vector' class and + then re-defined the class, the value of 'vectors' would have been the + empty list. + + + Various helper functions allow you to peruse the class hierarchy and + definitions. + + (sos-show-method ) + shows the method associated with . is a class name + (symbol) and is a class name or object. + + (sos-get-method ) + like sos-show-method, above, except that it returns the + method definition. + + (sos-get-classes) + returns the list of defined classes. + + (sos-get-inheritance ) + returns class names of the specified class and its parents (in order). + + (sos-get-class-definition ) + returns shows all the methods, data, etc. for the specified class or + object. + + (sos-class-of ) returns the class name of the object instance. + + + Implementation Notes + ==================== + + Objects are vectors, the first (zero'th) element being the + class-object (also a vector, shared by all instances of the class), + and the subsequent elements are the object data slots. + + Class definitions are a-lists accessed through a property of the class + name symbol. Class objects are established at scheme initialization + time, and contain type info used in dynamic operation, such as the + list of class methods, a link to the parent class object, etc. + + Methods are found in associated class objects, and are cached on + invocation. There is one cache per named method (all methods named + "foo" use the same cache, regardless of which class defines foo + methods. The cache is a vector containing pairs: (class-id . + procedure). If the object invoking the method has a class id of 3, the + third element of the cache (modulo the cache size) is examined to + see if the car of the pair is 3. If it is, you have a cache hit, and + the associated procedure is invoked. If there is a miss, the method is + looked up through the class objects, installed in the cache, and + invoked. + + When a class is defined, code is generated which conditionally + allocates the cache for each method in the class. That is, if the + method cache already exists, it is re-initialized instead of + re-allocated. The effect is that when re-defined methods are invoked + on existing objects, the resulting cache miss causes the new method to + be found and used. + + SEND is a macro which calls the dispatcher to examine the cache, then + invokes the returned procedure on the arguments. The cache is found as + a property of the method name symbol. + + A "super" call is done by invoking INHERITED-METHOD with the method + name and arguments. The parent id is obtained through the class + objects, and the method cache examined with this id instead of the + class id of the invoking object. + + Code in a method definition refers to slots as lexically bound + variables. These references are replaced with the appropriate vector + references on the sly (or, in the case of class variables, a reference + to a promise which will find and cache the binding location in the + appropriate class object). The substitution of references is somewhat + complex. The problem is that a code walker must identify references + which are NOT shadowed by binding forms in the method definition (such + as let, let*, letrec, do, and lambda). In this implementation, the + EXPAND function is called on the method definitions to macro-expand + the code. Since this function expands all special forms to lambda + expressions of one sort or another, the code walker need only + recognize variable shadowing resulting from formal parameters to + lambda expressions. + + Constructors/destructors follow C++ semantics. That is, constructors + for each class in the inheritance chain of the constructed object are + called in order, from most speciallized class to the primal class. The + destructors are called in the opposite order. Each + constructor/destructor is responsible for passing arguments up to its + parent class constructor/destructor. + + Slots have a required initial value and optional accessor name. For + example, a class declaration section: "(data (x 0 x-slot))" implies + that the object has a slot called X, which can be read using the + X-SLOT method, and written using the X-SLOT! method. + + + Design Rationale + ================ + + Main goals: compact size, high performance, transportability, in that + order (yeah, yeah, don't we all want that?). In this case, "compact + size" means small objects and small generated code, and don't worry + about the size of the implementation. High performance means method + calls within 3x of procedure calls. Transportability means written in + Scheme, WITHOUT using Scheme->C features THAT CAN'T BE DUPLICATED IN + SCHEME on other systems. + + A significant influence on the final design was the assumption that we + would be in a compiled-Scheme environment. That is, structuring of the + solution would assume that the object system run-time support could be + compiled. As we discovered, the best solution for a compiled run-time + system is not necessarily the best for an interpreted one. We picked + the fastest, without worrying about having to use the system in an + interpreted-only environment. + + The main design goal for SOS was compact size of objects as well as + generated code. Representing an object as a closure (as in several + existing systems) is simple and efficient, but very space-consuming. + The MMS application (a main target) has VERY large data space, so in + the final analysis, performance hinges on the paging/gc issues. In + light of this, the vector was selected for object representation. Many + subsequent design aspects derive from this one selection, such as + the technique of lexical substitution for data slot references. + + A second decision was to select a data structure to be used for method + dispatch. Two techniques can be used to organize a method search. The + first is to organize all the methods on a class basis, and point to + this structure from the object body. Given this structure, the problem + is to find the method using the method name found in the SEND macro. + The number of methods available to a highly specialized class might be + very large, creating a large search space. One could use an a-list + search, binary (or some classical) search, or a hash-table. A pure + a-list search lengthens linearly with the number of methods, but at + least the ordering of the a-list can reflect the shadowing of methods + down the hierarchy. The problem with a hash table is that you end up + with all the specialized versions of a method in one bucket, requiring + that you do a secondary search in many cases (an a-list as a secondary + structure could be used to implement shadowing). Other search + techniques which use the method name as a key suffer the same + drawbacks as the hash table version. + + I selected a second search technique based on a method organization + rather than class organization (ie. all methods named "foo" in one + structure). This results in more, smaller search spaces in which it's + possible to use the class id of the invoking object to select from + among a relatively small number of methods. This technique allows a + cache vector indexed by class id (modulo cache size), which has a + number of good charactoristics. It has fairly constant performance over + a varying number of methods. It also makes lazy updating easy in the + face of dynamic re-definition of classes (you just clear the cache + when one of the methods gets re-defined). + + The cache for a method is found on a property of the method + name symbol. One could use a hash table as well. However, the + absolute fastest way to find the cache structure is to bind it as the + value of the method symbol name. This puts method names in the global + namespace, and since we forsee having LOTS of methods, we chose not to + do this. + + Having a shared generic method dispatcher rather than creating + closure-based objects has another advantage - ALL of the dispatch + mechanism can be compiled. When this is done, interpreted methods are + dispatched faster than interpeted closure-objects, and about as fast + as interpreted procedure calls (in the 28sep90 system, at least). + + + Porting considerations + ====================== + + The code has been written to be as portable as possible, with the + following exceptions. This package uses property lists (getprop, + putprop). The few macros are written in the expansion-passing style + found in the Scheme->C system, but can easily be adapted to other + macro syles. This package also uses a code expander to expand all + special forms into lambda expressions before doing lexical + substitution. This was done to make the code walker manageable. The + expand code can be duplicated by concatenating the expander modules + from the Scheme->C interpreter system (gatekeeper.dec.com). Delay and + force are used, but don't seem to be in p1178/d5. These can be easily + implemented from examples in SICP. Lastly, some of the helper + functions use pp (pretty-printer), which can simply be removed. + + + Performance + =========== + + Non-transportable optimizations abound, but they would only be + considered as a last resort. The following are suggestions which might + improve performance. They fall into two categories: optimizing how to + get to the cached method, and optimizing what happens when there's a + cache miss. Obviously, which tack is best depends on an analysis of + actual applications and their cache hit rates. + + Making cache misses more tolerable: + + Use memoization tables for the cache (they are more like hash tables), + and perhaps use something else as the class-id, like a random seed. + See the paper on PCL: "Efficient Method Dispatch in PCL" by Kiczales + and Rodriguez. + + Optimize the class-object structure to make method location faster. + I have already done quite a bit here, and I doubt it could be made + much better for the average case (just saying this makes it, of + course, not true ;-). + + Finding the cached method faster: + + Generate the dispatch code in-line, avoiding one function call + entirely. Leads to larger generated code and possibly slower + interpreted performance. + + Compile the dispatcher procedure (SOS-SEND) with type-checking + turned off, after putting in the minimally required explict type + checks. Examination of the compiled code indicates that this might be + the largest single win. + + Store the object id in the object itself. This eliminates one vector + reference, but expands the object size. One could locate the class + object via class id (as I did originally), but this slows the + cache miss processing. This stuff is pretty much in the noise. + + + + Still to Do + =========== + + Decide on whether to allow data variable shadowing. + Check shadowed methods for argument list compatability? + Consider multiple inheritance, I suppose. + + + Wayne Allen, MCC 8/9/91 + *** /dev/null Mon Nov 30 15:31:12 1992 --- contrib/mcc-sos.sc Fri Nov 20 15:02:00 1992 *************** *** 0 **** --- 1,917 ---- + ;/* + ; * Copyright (c) 1991 + ; * Microelectronics and Computer Technology Corporation (MCC) + ; * All rights reserved. + ; * + ; * Use and copying of this software and preparation of derivative works + ; * based upon this software are permitted. However, any distribution of + ; * this software or derivative works must include the above copyright + ; * notice. + ; * + ; * This software is made available AS IS, and the MCC makes no + ; * warranty about the software, its performance or its conformity to + ; * any specification. + ; * + ; */ + + + ;; SOS - Scheme Object System + ;; (Wabjects!) + ;; + ;; Wayne Allen, 8/16/91 + ;; + ;; See sos-test.sc for usage... + ;; + ;; Transportablity issues: look at getprop/putprop, expand (see sos.doc), + ;; pp, delay/force, define-macro, define-constant. + ;; + ;; Future developments: multiple inheritance (maybe), perhaps a meta-object + ;; protocol of some sort. + ;; + + + (module mcc-sos + (top-level + *sos-classes* + *sos-method-cache-size* + *sos-method-cache-hits* + *sos-method-cache-misses* + + sos-class-of + sos-get-method + sos-show-method + sos-get-class-definition + sos-get-classes + sos-get-inheritance + + sos-define-class + sos-define-class-property + sos-make-object + sos-init-class + sos-class-property + sos-class-property! + sos-check-class-syntax + sos-check-declare-method-syntax + sos-add-method + sos-class-var-binding + sos-parent-constructor + sos-parent-destructor + sos-send + sos-send-super ) + ) + + + ;; these shouldn't have to be declared... Oh, well... + (define-external (EXPAND x) scexpand) + (define-external (PP x) scrt6) + + + (define *sos-classes* '()) + (define-constant *sos-method-cache-size* 8) + (define *sos-method-cache-hits* 0) + (define *sos-method-cache-misses* 0) + (define *sos-next-class-id* 0) + (define *sos-class-var-promises* '()) + + + ;;------------------------------------------- class object accessing constants + + (define-constant *sos-class-object-id* 0) + (define-constant *sos-class-object-parent* 1) + (define-constant *sos-class-object-method-list* 2) + (define-constant *sos-class-object-name* 3) + (define-constant *sos-class-object-vars* 4) + (define-constant *sos-class-object-constructor* 5) + (define-constant *sos-class-object-destructor* 6) + (define-constant *sos-class-object-size* 7) + + + ;;-------------------------------------------------------- check-parent-syntax + + (define (check-parent-syntax section) + ;; (parent ) + (cond ((and (pair? section) + (symbol? (car section)) + (null? (cdr section)) )) + (else + (display "*** badly formed parent specification ") (display section) + (newline) + #f ))) + + + ;;------------------------------------------------------- check-methods-syntax + + (define (check-methods-syntax section) + ;; (methods ( ( ...) ) ... ) + (and (pair? section) + (let next-method ((methods section)) + (cond ((null? methods)) + (else (let ((method (car methods))) + (cond ((and (pair? method) + (symbol? (car method)) + (pair? (cdr method)) + (or (symbol? (car method)) + (null? (cadr method)) + (pair? (cadr method)) ) + (list? (cddr method)) ) + (next-method (cdr methods)) ) + (else + (display "*** badly formed method definition ") + (display method) (newline) + #f )))))))) + + + ;;---------------------------------------------------------- check-data-syntax + + (define (check-data-syntax section) + ;; (data ( []) ... ) + ;; OR + ;; (data ( {[reader ] + ;; [writer ]} ) ... ) + ;; + ;; (class-data ( []) ... ) + ;; OR + ;; (class-data ( {[reader ] + ;; [writer ]} ) ... ) + (and (pair? section) + (let next-datum ((data section)) + (cond ((null? data)) + (else (let ((datum (car data))) + (cond ((and (pair? datum) + (symbol? (car datum)) + (pair? (cdr datum)) + (or (null? (cddr datum)) + (and (pair? (cddr datum)) + (symbol? (caddr datum)) + (or (and (cdddr datum) + (or (eq? (caddr datum) 'reader) + (eq? (caddr datum) 'writer)) + (symbol? (cadddr datum)) + (or (and (cddddr datum) + (or (eq? (list-ref datum 4) 'reader) + (eq? (list-ref datum 4) 'writer)) + (list-tail datum 5) + (symbol? (list-ref datum 5)) + (null? (list-tail datum 6))) + (null? (cddddr datum)))) + (null? (cdddr datum)))))) + (next-datum (cdr data)) ) + (else + (display "*** badly formed data definition ") + (display datum) (newline) + #f )))))))) + + + ;;--------------------------------------------------- check-constructor-syntax + + (define (check-constructor-syntax section) + ;; (constructor ( ...) ( ...) ) + (cond ((and (pair? section) + (or (symbol? (car section)) + (null? (car section)) + (pair? (car section)) ) + (pair? (cdr section)) + (or (null? (cadr section)) + (pair? (cadr section)) ) + (list? (cddr section)) )) + (else + (display "*** badly formed constructor/destructor definition ") + (display section) (newline) + #f ))) + + + ;;-------------------------------------------- sos-check-declare-method-syntax + + (define (sos-check-declare-method-syntax form) + ;; ( ( ...) ) + (if (< (length form) 4) + (error 'DECLARE-METHOD "bad method declaration: ~S" form) ) + (cond ((and (symbol? (car form)) + (symbol? (cadr form)) + (or (and (memq (car form) '(constructor destructor)) + (check-constructor-syntax (cdr form)) ) + (check-methods-syntax (list (cdr form))) ))) + (else + (error 'SOS-CHECK-DECLARE-METHOD-SYNTAX "badly formed declaration") ))) + + + ;;----------------------------------------------------- sos-check-class-syntax + + (define (sos-check-class-syntax obj) + ;; tries to recover from errors and check all sections + (if (< (length obj) 2) + (error 'SOS-CHECK-CLASS-SYNTAX "bad object definition: ~S" obj) ) + (let ((ok-so-far (if (symbol? (car obj)) + #t + (begin (display "*** class name must be a symbol") + (display (car obj))(newline) + #f )))) + (let next-section ((sections (cdr obj))) + (cond (sections + (let ((section (car sections))) + (set! ok-so-far + (and (pair? section) + (case (car section) + ((parent) (check-parent-syntax (cdr section))) + ((methods) (check-methods-syntax (cdr section))) + ((data) (check-data-syntax (cdr section))) + ((class-data) (check-data-syntax (cdr section))) + ((constructor destructor) + (check-constructor-syntax (cdr section))) + (else #f) ) + ok-so-far ))) + (next-section (cdr sections)) ))) + (if (not ok-so-far) + (error 'SOS-CHECK-CLASS-SYNTAX "Badly formed class definition") ))) + + + ;;------------------------------------------------------------------ attribute + + (define (attribute a l) + (if (null? l) + '() + (let ((p (assq a l))) + (if p (cdr p) '()) ))) + + ;;----------------------------------------------------------------- attribute! + + (define (attribute! a v l) + (if (null? l) + (list (cons a v)) + (let ((p (assq a l))) + (cond (p (set-cdr! p v) l) + (else (cons (cons a v) l)))))) + + + ;;--------------------------------------------------------- sos-class-property + + (define (sos-class-property class property) + (attribute property (getprop class '*sos-class-definition*)) ) + + + ;;--------------------------------------------------------- sos-class-property! + + (define (sos-class-property! class property value) + (putprop class '*sos-class-definition* + (attribute! property value (getprop class '*sos-class-definition*)))) + + + ;;------------------------------------------------------------- sos-add-method + + (define (sos-add-method form) + (let ((method-name (car form)) + (class (cadr form)) + (method-body (cddr form)) ) + (if (not (getprop class '*sos-class-definition*)) + (error 'SOS-ADD-METHOD "class not yet declared: ~S" class) ) + (case method-name + ((constructor destructor) + (let ((constructor (sos-class-property class method-name))) + (if constructor + ;; replace existing constructor/destructor + (set-cdr! constructor method-body) + ;; add new definition + (sos-class-property! class method-name method-body) ))) + (else + (let* ((methods (sos-class-property class 'methods)) + (method (assq method-name methods)) ) + (if method + ;; replace existing definition + (set-cdr! method method-body) + ;; add definition + (sos-class-property! class 'methods + (cons (cons method-name method-body) + methods )))))))) + + + ;;--------------------------------------------------------- alloc-method-cache + + (define (alloc-method-cache method) + (let ((cache (getprop method '*sos-method-cache*))) + (if cache + ;; clear existing cache to force re-fetch of methods. This allows + ;; re-definition of methods without disturbing existing objects. + (do ((null-pair (list -1)) + (i 0 (+ i 1))) + ((>= i *sos-method-cache-size*)) + (vector-set! cache i null-pair) ) + ;; allocate new cache... + (let ((method-cache (make-vector (+ *sos-method-cache-size* 1) '(-1)))) + ;; store method name at end of cache for easy identification... + (vector-set! method-cache *sos-method-cache-size* method) + (putprop method '*sos-method-cache* method-cache) )))) + + + ;;------------------------------------------------------------------ get-slots + + (define (get-slots class type) + (let loop ((slot-list '()) + (class (list class)) ) + (if class + (loop (append slot-list + (reverse (sos-class-property (car class) type))) + (sos-class-property (car class) 'parent)) + slot-list ))) + + + ;;---------------------------------------------------------- get-instance-slots + + (define (get-instance-slots class) + (get-slots class 'data) ) + + + ;;------------------------------------------------------------- get-class-slots + + (define (get-class-slots class) + (get-slots class 'class-data) ) + + + ;;------------------------------------------------ substitute-lambda-reference + + (define (substitute-lambda-reference var-name structure reader writer) + + ;; structure -> ((a b c) ) + + ;; we substitute 'var-name' references in if the var is not in + ;; the lambda's arg list. Returns #f if no possible substitution, + ;; otherwise returns value of SUBSTITUTE-REFERENCE call. + + (let ((args (car structure)) + (body (cdr structure)) ) + (cond ((null? args) + (substitute-reference var-name body reader writer) ) + ((pair? args) + (let loop ((args args)) + (cond ((null? args) ;; no shadow vars found... + (substitute-reference var-name body reader writer) ) + ((pair? args) + (if (not (eq? var-name (car args))) + (loop (cdr args)) ;keep looking... + #f )) ;found shadowed variable + ((symbol? args) ;last one in list... + (if (not (eq? var-name args)) + (substitute-reference + var-name body reader writer) + #f ))))) + ((symbol? args) + (if (not (eq? var-name args)) + (substitute-reference var-name body reader writer) + #f ))))) + + + ;;------------------------------------------------------- substitute-reference + + (define (substitute-reference var-name structure reader writer) + + ;; destructively substitutes reader/writer expression for var-name. + ;; Returns #t if substitution made, otherwise #f. READER completely + ;; replaces var references, while WRITER replaces the '(set! ' + ;; portion of set! references. See SUBSTITUTE-INSTANCE-VARS for example. + + (cond + ((null? structure) #f) + ((pair? structure) + (let ((car-substitution + (cond + ((pair? (car structure)) + (cond ((equal? (caar structure) 'quote) + #f ) + ((equal? (caar structure) 'lambda) + ;; substitute (lambda (...) ...) reference + (substitute-lambda-reference + var-name (cdar structure) reader writer )) + ((and (equal? (caar structure) 'set!) + (equal? (cadar structure) var-name) ) + ;; substitute (set! ) reference, + ;; first substituting in the value part... + (substitute-reference var-name (caddar structure) + reader writer) + (set-car! structure (append writer (cddar structure))) + #t) + (else + (substitute-reference var-name (car structure) + reader writer) ))) + ((equal? (car structure) var-name) + ;; substitute reference + (set-car! structure reader) + #t) + (else + (substitute-reference var-name (car structure) reader writer) ))) + (cdr-substitution + (substitute-reference var-name (cdr structure) reader writer) )) + (or car-substitution cdr-substitution) )) + (else #f) )) + + + ;;--------------------------------------------------- substitute-instance-vars + + (define (substitute-instance-vars slots body ) + (let next-slot ((slots slots) + (index (length slots))) + (cond (slots + (substitute-reference + (caar slots) body + `(vector-ref self ,index) + `(vector-set! self ,index) ) + (next-slot (cdr slots) (- index 1)) ) + (else body )))) + + + ;;------------------------------------------------------ substitute-class-vars + + (define (substitute-class-vars class slots body ) + (for-each + (lambda (slot) + (if (substitute-reference (car slot) body + `(cdr (force ,(car slot))) + `(set-cdr! (force ,(car slot))) ) + (if (not (assq (car slot) *sos-class-var-promises*)) + ;; construct promise for use in accessing. + ;; See GENERATE-METHOD-DEFINITIONS + (set! *sos-class-var-promises* + (cons + `(,(car slot) + (delay (sos-class-var-binding ',class ',(car slot))) ) + *sos-class-var-promises* ))))) + ;; remove shadowed vars from slots so we substitute only once... + (let loop ((old slots) + (new '()) ) + (if (null? old) + new + (if (assq (caar old) new) + (loop (cdr old) new) + (loop (cdr old) (cons (car old) new) )))))) + + + ;;----------------------------------------------------------- substitute-calls + + (define (substitute-calls structure) + + ;; replaces '(inherited-method ...)' + ;; with '((sos-send-super self ) ...)' + ;; We could do this with a macro, but then it could be mistakenly expanded + ;; outside of method definitions. + + (cond ((null? structure)) + ((pair? structure) + (cond ((and (pair? (car structure)) + (equal? (caar structure) 'inherited-method) + (> (length (car structure)) 1) ) + (substitute-calls (cdar structure)) + (set-car! structure + `((sos-send-super self ',(cadar structure)) + ,@(cddar structure) ))) + (else + (substitute-calls (car structure)) )) + (substitute-calls (cdr structure)) ))) + + + ;;------------------------------------------------------------- rewrite-method + + (define (rewrite-method class instance-slots class-slots body) + + ;; A method is "rewritten" to replace instance and class variable references + ;; with function calls which "do the right thing". Inherited method calls + ;; ("super" calls) are also replaced. + + (substitute-calls body) + (substitute-instance-vars instance-slots body) + (substitute-class-vars class class-slots body) ) + + + ;;----------------------------------------------------- sos-parent-constructor + + (define (sos-parent-constructor class) + + ;; called from constructors only if there IS a parent, so no checking done. + + (vector-ref + (force (vector-ref (getprop class '*sos-class-object*) + *sos-class-object-parent* )) + *sos-class-object-constructor* )) + + + ;;----------------------------------------------------- sos-parent-destructor + + (define (sos-parent-destructor class) + + ;; called from destructors only if there IS a parent, so no checking done. + + (vector-ref + (force (vector-ref (getprop class '*sos-class-object*) + *sos-class-object-parent* )) + *sos-class-object-destructor* )) + + + ;;-------------------------------------------------------- generate-destructor + + (define (generate-destructor class) + + ;; generate code defining destructor method. User-specified destruction + ;; code is placed BEFORE call to parent class's destructor. + + (let* ((parent (sos-class-property class 'parent)) + (destructor (cond ((sos-class-property class 'destructor)) + (else '(()()#t)) )) + (dargs (car destructor)) + (pargs (cadr destructor)) + (body (map expand (cddr destructor))) ) + (rewrite-method class (get-instance-slots class) + (get-class-slots class) body ) + `(cons 'destructor (lambda ,(cons 'self dargs) + ,@body + ,(if parent + `((sos-parent-destructor ',class) self ,@pargs) ) + self )))) + + + ;;------------------------------------------------------------ sos-make-object + + (define (sos-make-object class size initializer args) + + ;; generic object constructor - allocates storage, installs class object + ;; pointer, and calls class constructor + + (let ((new-object (make-vector (+ 1 size)))) + (vector-set! new-object 0 (getprop class '*sos-class-object*)) + (apply initializer new-object args) )) + + + ;;------------------------------------------------------- generate-constructor + + (define (generate-constructor class) + + ;; generates code defining constructor method. This includes initialization + ;; of class's instance slots. The order of construction is 1. parent class + ;; constructor, 2. instance variable initialization, and 3. user-specified + ;; constructor code. + + (let* ((instance-slots (get-instance-slots class)) + (local-instance-slots (sos-class-property class 'data)) + (parent (sos-class-property class 'parent)) + (constructor (cond ((sos-class-property class 'constructor)) + (else '(()()#t)) )) + (cargs (car constructor)) + (pargs (cadr constructor)) + (body (map expand (cddr constructor))) ) + (let loop ((slot-index (length instance-slots)) + (slots (reverse local-instance-slots)) + (init-forms '()) ) + (if slots + (loop (- slot-index 1) (cdr slots) + (cons (list 'vector-set! 'self slot-index (cadar slots)) + init-forms )) + (begin + (rewrite-method class instance-slots + (get-class-slots class) body ) + (let ((constructor `(lambda ,(cons 'self cargs) + ,(if parent + `((sos-parent-constructor ',class) + self ,@pargs )) + ,@init-forms ,@body self ))) + `(let ((initializer ,constructor)) + (set-top-level-value! + ',(string->symbol (string-append "MAKE-" + (symbol->string class))) + (lambda args + (sos-make-object ',class ,(length instance-slots) + initializer args ))) + (cons 'constructor initializer) ))))))) + + + ;;------------------------------------------------- generate-method-definition + + (define (generate-method-definition class method instance-slots class-slots) + + ;; generate code for defining method + + (let ((method-name (car method)) + (args (cadr method)) + (body (map expand (cddr method))) ) + (rewrite-method class instance-slots class-slots body) + `(cons ',method-name ,(append (list 'lambda (cons 'self args)) body)) )) + + + ;;--------------------------------------------------------- generate-accessors + + (define (generate-accessors slots) + + ;; creates list of accessors for specified slots. + + (let next-slot ((slots slots) + (accessors '()) ) + (if slots + (if (<= (length (car slots)) 2) + (next-slot (cdr slots) accessors) + (let* ((accessor-specs (cddar slots)) + (reader-spec (and (cdr accessor-specs) + (memq 'reader accessor-specs))) + (writer-spec (and (cdr accessor-specs) + (memq 'writer accessor-specs))) + (reader-name (if (cdr accessor-specs) + (and reader-spec + (cdr reader-spec) + (cadr reader-spec)) + (car accessor-specs))) + (writer-name (if (cdr accessor-specs) + (and writer-spec + (cdr writer-spec) + (cadr writer-spec)) + (string->symbol + (string-append + (symbol->string reader-name) "!"))))) + (if reader-name + (set! accessors (cons (list reader-name '() (caar slots)) + accessors))) + (next-slot (cdr slots) + (if writer-name + (cons (list writer-name '(v) + (list 'set! (caar slots) 'v)) + accessors) + accessors)))) + accessors ))) + + + ;;------------------------------------------------ generate-method-definitions + + (define (generate-method-definitions class) + + ;; directs generation of all code for a class. Method definitions for + ;; class methods, constructor, destructor, slot accessors, and class + ;; variable promises are concantenated into one big list, which is + ;; passed in the end to SOS-INIT-CLASS (see also, SOS-DEFINE-CLASS). + + (set! *sos-class-var-promises* '()) + (let* ((instance-slots (get-instance-slots class)) + (class-slots (get-class-slots class)) + (method-defs + (cons 'list + (cons (generate-constructor class) + (cons (generate-destructor class) + (map (lambda (method) + (generate-method-definition + class method + instance-slots class-slots)) + (append + (generate-accessors instance-slots) + (generate-accessors class-slots) + (sos-class-property class 'methods) ))))))) + (list 'let *sos-class-var-promises* method-defs) )) + + + ;;----------------------------------------------------------- class-object-set + + + (define (class-object-set! class-object class parent class-vars id methods) + + ;; set the values of the specified class object. A class object is a + ;; vector, referenced in the code via offset constants (for efficiency). + + ;; class id is used for indexing into method cache... + (vector-set! class-object *sos-class-object-id* id) + ;; use a promise for caching pointer to parent's class object. This allows + ;; us to permit classes to be DECLARED and DEFINED in any order (this means + ;; some inheritance errors are detected at run-time.) + (if (null? parent) + (vector-set! class-object *sos-class-object-parent* (delay #f)) + (vector-set! class-object *sos-class-object-parent* + (delay (getprop (car parent) '*sos-class-object* )))) + ;; method look-ups look here... + (vector-set! class-object *sos-class-object-method-list* methods) + (vector-set! class-object *sos-class-object-name* class) + ;; For each class variable on a-list, replace the cdr of it's var/value + ;; pair (if present) so as not to disturb already-forced promises. See + ;; SUBSTITUTE-CLASS-VARS. + (for-each (lambda (v) + (if (not (assq (car v) (vector-ref class-object + *sos-class-object-vars* ))) + (vector-set! class-object *sos-class-object-vars* + (cons (cons (car v) (eval (cadr v))) + (vector-ref class-object + *sos-class-object-vars* ))))) + class-vars ) + ;; put constructors/destructors here for fast access + (vector-set! class-object *sos-class-object-constructor* + (cdr (assq 'constructor methods)) ) + (vector-set! class-object *sos-class-object-destructor* + (cdr (assq 'destructor methods)) ) + class-object ) + + + ;;------------------------------------------------------------- sos-init-class + + (define (sos-init-class class parent class-data methods ) + (for-each (lambda (method) (alloc-method-cache (car method))) methods) + (set! *sos-next-class-id* (+ *sos-next-class-id* 1)) + (class-object-set! + (or (getprop class '*sos-class-object*) ;class object exists... + (let ((class-object (make-vector *sos-class-object-size*))) + (set! *sos-classes* (cons class *sos-classes*)) + (putprop class '*sos-class-object* class-object) + class-object )) + class parent class-data *sos-next-class-id* methods ) + class ) + + + ;;------------------------------------------------------------ sos-define-class + + (define (sos-define-class class-names) + ;; check out define-class macro... + (map (lambda (class-name) + (if (null? (getprop class-name '*sos-class-definition*)) + (error 'sos-define-class "Class not declared: ~S" class-name) + `(sos-init-class ',class-name + ',(sos-class-property class-name 'parent) + ',(sos-class-property class-name 'class-data) + ,(generate-method-definitions class-name) ))) + class-names )) + + + ;;--------------------------------------------------- sos-define-class-property + + (define (sos-define-class-property class-names) + ;; check out define-private-class macro... + (map (lambda (class-name) + (let ((def (getprop class-name '*sos-class-definition*))) + (if def + `(putprop ',class-name '*sos-class-definition* ',def) + (error 'sos-define-class "Class not declared: ~S" class-name) ))) + class-names )) + + + ;;------------------------------------------------------- sos-class-var-binding + + (define (sos-class-var-binding class slot) + + ;; look up bindings of class var in class object(s) + + (letrec ((var-binding + (lambda (class-object) + (if (not class-object) + (error 'CLASS-VAR-BINDING "No such class variable: ~s" slot) + (let ((binding + (assq slot (vector-ref class-object + *sos-class-object-vars* )))) + (if binding + binding + (var-binding + (force (vector-ref class-object + *sos-class-object-parent* ))))))))) + + (var-binding (getprop class '*sos-class-object*)) )) + + + ;;---------------------------------------------------------------- find-method + + (define (find-method method class-object) + + ;; look up method in class object(s) + + (if class-object + (let ((procedure (assq method + (vector-ref class-object + *sos-class-object-method-list*)))) + (if procedure + (cdr procedure) + (find-method method + (force (vector-ref class-object + *sos-class-object-parent* ))))))) + + + ;;--------------------------------------------------------------- cache-method + + (define (cache-method method-name method-cache class-object) + (let ((method (find-method method-name class-object))) + (set! *sos-method-cache-misses* (+ *sos-method-cache-misses* 1)) + (cond (method + (vector-set! method-cache + (remainder (vector-ref class-object + *sos-class-object-id*) + *sos-method-cache-size*) + (cons class-object method) ) + method ) + (else + (error 'CACHE-METHOD + "Specified method not defined for this class" ))))) + + + ;;------------------------------------------------------------------- sos-send + + (define (sos-send self method-name) + + ;; look up and dispatch method. + + (let* ((method-cache (getprop method-name '*sos-method-cache*)) + (cache-pair + (vector-ref method-cache + (remainder (vector-ref (vector-ref self 0) + *sos-class-object-id*) + *sos-method-cache-size*) ))) + (cond ((eq? (vector-ref self 0) (car cache-pair)) + ;; method is cached!! + (set! *sos-method-cache-hits* (+ *sos-method-cache-hits* 1)) + (cdr cache-pair) ) + ;; cache miss!! Find and install method... + (else + (cache-method method-name method-cache (vector-ref self 0)) )))) + + + ;;------------------------------------------------------------- sos-send-super + + (define (sos-send-super self method-name) + + ;; look up and return method via parent class's id. + + (let ((parent (force (vector-ref + (vector-ref self 0) *sos-class-object-parent* )))) + (if (not parent) + (error 'SOS-SEND-SUPER "No parent class available") + (let* ((method-cache (getprop method-name '*sos-method-cache*)) + (cache-pair (vector-ref + method-cache + (remainder + (vector-ref parent *sos-class-object-id*) + *sos-method-cache-size*) ))) + (cond ((eq? parent (car cache-pair)) + ;; method is cached!! + (set! *sos-method-cache-hits* (+ *sos-method-cache-hits* 1)) + (cdr cache-pair) ) + ;; cache miss!! Find and install method... + (else + (cache-method method-name method-cache parent) )))))) + + + + ;;============================================================================ + ;; + ;; Just a bunch of helper functions for examining classes... + ;; + ;;---------------------------------------------------------------- sos-class-of + + (define (sos-class-of obj) + (or (and (symbol? obj) (member obj *sos-classes*) obj) + (and (vector? obj) + (> (vector-length obj) 0) + (vector? (vector-ref obj 0)) + (>= (vector-length (vector-ref obj 0)) *sos-class-object-size*) + (member (vector-ref (vector-ref obj 0) *sos-class-object-name*) + *sos-classes*) + (vector-ref (vector-ref obj 0) *sos-class-object-name*) ))) + + + ;;--------------------------------------------------- sos-get-class-definition + + (define (sos-get-class-definition obj) + (let ((class (sos-class-of obj))) + (if (not (symbol? class)) + #f + (getprop class '*sos-class-definition*) ))) + + + ;;------------------------------------------------------------ sos-get-classes + + (define (sos-get-classes) + *sos-classes* ) + + + ;;-------------------------------------------------------- sos-get-inheritance + + (define (sos-get-inheritance obj) + (let ((class (sos-class-of obj))) + (letrec ((find (lambda (class) + (if (null? class) + '() + (cons (car class) + (find (sos-class-property (car class) + 'parent ))))))) + (find (or (and class (list class)) '())) ))) + + + ;;------------------------------------------------------------- sos-get-method + + (define (sos-get-method method obj) + (let ((class (sos-class-of obj))) + (letrec ((find + (lambda (method-name class) + (if (null? class) + '() + (let ((method + (assq method-name + (sos-class-property (car class) 'methods) ))) + (if method + (cons (car class) method) + (find method-name + (sos-class-property (car class) + 'parent )))))))) + (find method (or (and class (list class)) '())) ))) + + + ;;------------------------------------------------------------ sos-show-method + + (define (sos-show-method method obj) + (let ((method (sos-get-method method obj))) + (cond (method + (display "From class ") (display (car method)) (display ": ") + (newline) + (pp (cdr method)) )))) + *** /dev/null Mon Nov 30 15:31:14 1992 --- contrib/mcc-sos.sch Fri Nov 20 15:02:02 1992 *************** *** 0 **** --- 1,127 ---- + ;/* + ; * Copyright (c) 1991 + ; * Microelectronics and Computer Technology Corporation (MCC) + ; * All rights reserved. + ; * + ; * Use and copying of this software and preparation of derivative works + ; * based upon this software are permitted. However, any distribution of + ; * this software or derivative works must include the above copyright + ; * notice. + ; * + ; * This software is made available AS IS, and the MCC makes no + ; * warranty about the software, its performance or its conformity to + ; * any specification. + ; * + ; */ + + + ;; SOS - Scheme Object System + ;; (Wabjects!) + ;; + ;; Wayne Allen, 8/16/91 + ;; + ;; See sos-test.sc for usage... + + + ;;===================================================================== macros + + + ;;----------------------------------------------------------------------- send + + (define-macro send + + ;; (send [ ...]) + + (lambda (form expander) + (if (< (length form) 3) + (error 'SEND "badly formed send expression: ~S" form) + (expander `((sos-send ,(cadr form) ',(caddr form)) + ,(cadr form) ,@(cdddr form) ) + expander )))) + + + ;;-------------------------------------------------------------- declare-class + + (define-macro declare-class + + ;; (declare-class + ;; [(parent )] + ;; [(data ( []) ... )] + ;; [(class-data ( []) ... )] + ;; [(methods ( ( ...) ) ...)] + ;; [(constructor ( ...) ( ...) )] + ;; [(destructor ( ...) ( ...) )] + ;; ) + + (lambda (form expander) + (sos-check-class-syntax (cdr form)) + (putprop (cadr form) '*sos-class-definition* (cddr form)) + #t )) + + + ;;--------------------------------------------------------------- define-class + + (define-macro define-class + + ;; (define-class ...) + + (lambda (form expander) + (expander + `(begin ,@(sos-define-class-property (cdr form)) + ,@(sos-define-class (cdr form)) + ',(cdr form) ) + expander ))) + + + ;;------------------------------------------------------- define-private-class + + (define-macro define-private-class + + ;; (define-class ...) + + ;; this method does not expand the class definitions properties, and can + ;; be used to reduce generated code size for compiled classes, as well + ;; as make implementation information un-available in the run-time system. + + (lambda (form expander) + (expander + `(begin ,@(sos-define-class (cdr form)) + ',(cdr form) ) + expander ))) + + + ;;------------------------------------------------------------- declare-method + + (define-macro declare-method + + ;; (declare-method name class ( ...) ) + + ;; can be used to define methods for a class outside the class + ;; declaration. Can be used to add or change methods for a class at run-time, + ;; as long as DEFINE-CLASS is re-invoked for the class as well. + + (lambda (form expander) + (sos-check-declare-method-syntax (cdr form)) + (sos-add-method (cdr form)) + #t )) + + + + ;;================================================================ procedures + + ;; the expand function of the interpreter is call "sc-expand" in the compiler. + ;; Wish I knew why + + (eval-when (compile) + (set! expand sc-expand) ) + + (define-external (SOS-CLASS-VAR-BINDING class slot) mcc-sos) + (define-external (SOS-SEND self method-name) mcc-sos) + (define-external (SOS-PARENT-DESTRUCTOR class) mcc-sos) + (define-external (SOS-PARENT-CONSTRUCTOR class) mcc-sos) + (define-external (SOS-MAKE-OBJECT class size initializer args) mcc-sos) + (define-external (SOS-INIT-CLASS class parent class-data methods) mcc-sos) + + (eval-when (compile) + (load "mcc-sos.sc") ) + *** 1.2.1.2 1991/11/11 21:43:49 --- scrt/callcc.c 1992/11/30 16:24:33 *************** *** 87,92 **** --- 87,98 ---- #define setjmp(x) sc_setjmp(x) #endif + #ifdef HP700 + /* + #define longjmp(x, y) sc_longjmp(x, y) + #define setjmp(x) sc_setjmp(x) + */ + #endif TSCP sc_clink; /* Pointer to inner most continuation on stack. */ *************** *** 142,148 **** --- 148,158 ---- fp = sc_stackbase; else fp = (T_U(sc_clink))->continuation.address; + #ifdef hp9000s800 + count = ((unsigned)(STACKPTR)-(unsigned)(fp))/4; + #else count = ((unsigned)(fp)-(unsigned)(STACKPTR))/4; + #endif save_fp = fp; save_count = count; cp = sc_allocateheap( NULLCONTINUATIONSIZE+count+2+sc_maxdisplay, *************** *** 158,164 **** --- 168,178 ---- tp = &cp->continuation.word0; rcount = sc_maxdisplay; while (rcount--) *tp++ = (int)sc_display[ rcount ]; + #ifdef hp9000s800 + while (count--) *tp++ = *tos--; + #else while (count--) *tp++ = *tos++; + #endif MUTEXOFF; if (setjmp( cp->continuation.savedstate ) == 0) { callccresult = sc_apply_2dtwo( function, *************** *** 177,186 **** --- 191,271 ---- fp = &(T_U(sc_clink))->continuation.word0+sc_maxdisplay; count = (T_U(sc_clink))->continuation.length-sc_maxdisplay- NULLCONTINUATIONSIZE; + #ifdef hp9000s800 + while (count--) *tp-- = *fp++; + #else while (count--) *tp++ = *fp++; + #endif sc_clink = (T_U(sc_clink))->continuation.continuation; } } + tp = &T_U( callcccp )->continuation.word0; + rcount = sc_maxdisplay; + while (rcount--) sc_display[ rcount ] = (TSCP)(*tp++); + sc_clink = T_U( callcccp )->continuation.continuation; + sc_stacktrace = T_U( callcccp )->continuation.stacktrace; + /* Move result onto the stack under mutex */ + function = callccresult; + MUTEXOFF; + return( function ); + } + + + + static catchthrown( result, cp ) + TSCP result, cp; + { + TSCP clink; /* link follower */ + MUTEXON; + clink = sc_clink; + callccresult = result; + callcccp = cp; + /* Unwind CLINK to see if this continuation is currently on the + stack. */ + while (clink != EMPTYLIST) { + if (clink == cp) { + sc_clink = clink; + longjmp( (T_U(cp))->continuation.savedstate, 1 ); + } + clink = (T_U(clink))->continuation.continuation; + } + /* Continuation is not currently on the stack, so user has + tried to throw to non-existant catch. Error! */ + sc_error( "CATCH/THROW", "Throw target is not on the stack",0); + } + + + TSCP sc_catch_v; + + TSCP sc_catch( function ) + TSCP function; + { + SCP cp; /* Pointer to the continuation */ + + MUTEXON; + cp = sc_allocateheap( NULLCONTINUATIONSIZE+2+sc_maxdisplay, + CONTINUATIONTAG, + NULLCONTINUATIONSIZE+sc_maxdisplay ); + cp->continuation.continuation = sc_clink; + cp->continuation.stacktrace = sc_stacktrace; + sc_clink = U_TX( cp ); + cp->continuation.address = STACKPTR; + + tp = &cp->continuation.word0; + rcount = sc_maxdisplay; + while (rcount--) *tp++ = (int)sc_display[ rcount ]; + MUTEXOFF; + if (setjmp( cp->continuation.savedstate ) == 0) { + callccresult = sc_apply_2dtwo( function, + sc_cons( sc_makeprocedure( 1, 0, + catchthrown, + U_TX( cp ) ), + EMPTYLIST ) ); + sc_clink = T_U( sc_clink )->continuation.continuation; + return( callccresult ); + } + + /* Return here when the catch is thrown to (continuation is invoked). */ tp = &T_U( callcccp )->continuation.word0; rcount = sc_maxdisplay; while (rcount--) sc_display[ rcount ] = (TSCP)(*tp++); *** 1.2.1.2 1991/11/11 21:46:07 --- scrt/callcc.h 1991/11/13 19:57:22 *************** *** 42,47 **** --- 42,50 ---- /* This module implements CALL-WITH-CURRENT-CONTINUATION. SC_CLINK is a pointer to the current "inner most" continuation on the stack. + CATCH is identical to CALL-WITH-CURRENT-CONTINUATION, except that it's + only for upward throws (no stack is saved). That is, if a catch is + thrown to, and it isn't on the clink list, it's an error. */ extern TSCP sc_clink; *************** *** 51,54 **** --- 54,62 ---- extern TSCP sc_ntinuation_1af38b9f_v; extern TSCP sc_ntinuation_1af38b9f(); + + + extern TSCP sc_catch_v; + + extern TSCP sc_catch(); *** 1.2.1.2 1991/11/11 21:46:07 --- scrt/cio.c 1992/02/13 20:01:45 *************** *** 45,50 **** --- 45,53 ---- #include #include #include + #if defined(RS6000) + #include + #endif #include "objects.h" #include "cio.h" *************** *** 144,146 **** --- 147,282 ---- else return( 0 ); } + + + /************************************************************************ + * Modification from Microelectronics and Computer Technology Corp. (MCC) + * + * Author: Kevin Gourley 10/30/91 (gourley@mcc.com) + * + * This modification provides a function called system_task_select which behaves + * identically to the UNIX system select function, with the exception + * that its blocking (timeout) behavior may be controlled externally. + * (see explanation below) + */ + + #include + #include + #include "/usr/include/signal.h" + #include + + + /* + * These timeout values are used by system_task_select to control + * its blocking/non-blocking behavior. + */ + static struct timeval *sc_timeout = NULL; /* NULL value causes system_task_select to block */ + static struct timeval zero_sec_timeout = {0,0}; /* Zero second timeout (no blocking) */ + + + /* + * The previous registered SIGIO handler (if there was one) + */ + static void (*prev_SIGIO_handler)() = NULL; + + /***************************************************************************** + * system_task_block_select + * system_task_noblock_select + * system_task_noblock_select_on_SIGIO + * + * system_task_block_select is a function that requests the next select call + * (within system_task_select) to block, waiting for input, if nothing is available. + * + * system_task_noblock_select is a function that requests the next select call + * (within system_task_select) to NOT block, if no input is available. + * + * system_task_noblock_select_on_SIGIO is a function that registers a SIGIO + * signal handler that causes the next select call from within system_task_select + * to be a non-blocking select. The handler subsequently invokes the + * previously registered SIGIO handler. + * + * This is done because the UNIX select function ALREADY automatically + * falls out of its blocking state if a SIGIO signal occurs. SIGIO + * handlers sometimes perform a minimal set of operations (such as set a + * semaphore, read some data and put it in a buffer, etc.) and then + * rely on other code in the event loop to further process the data + * that is buffered or cached. BUT, if a SIGIO signal occurs during + * the small window of time directly BEFORE a call to select and + * AFTER other event handling is finished, then the select call won't + * automatically return and therefore it will sometimes block at + * undesirable times. Thus, this code implements the following + * model (which should be safe for most any event/signal handling): + * + * If a SIGIO signal occurs DURING a select call (within system_task_select), + * it falls out of its blocking state (as is always done by select). + * IF a SIGIO signal occurs PRIOR to a select call + * (within system_task_select), then the select will not block to wait + * for input, if none is available on a file descriptor. After the + * select call, the NEXT select call WILL block (unless + * system_task_noblock_select is again called via another SIGIO signal + * occurrence). + * + * Granted, not many applications need this capability, but it is + * totally transparent to the system file task and system idle task + * support, and it does not have to be used, but it IS necessary for + * some event processing applications. + */ + + void system_task_noblock_select_SIGIO_handler(sig, code, scp, addr) + int sig, code; + struct sigcontext *scp; + char *addr; + { + sc_timeout = &zero_sec_timeout; + if (prev_SIGIO_handler) (*prev_SIGIO_handler)(sig,code,scp,addr); + } + + void system_task_noblock_select_on_SIGIO() + { + static int SIGIO_handler_registered = 0; + + /* make sure we catch SIGIO's */ + if (!SIGIO_handler_registered) { + SIGIO_handler_registered = 1; + prev_SIGIO_handler = signal(SIGIO,system_task_noblock_select_SIGIO_handler); + } + } + + + /* system_task_noblock_select + * + * This function causes the NEXT system_task_select call to not block, if there + * is no input ready on any of the FD's. + */ + void system_task_noblock_select () + { + sc_timeout = &zero_sec_timeout; + } + + /* system_task_block_select + * + * This function causes the NEXT system_task_select call to block, waiting for input, + * if there is no input ready on any of the FD's. + */ + void system_task_block_select () + { + sc_timeout = (struct timeval *)NULL; + } + + /* system_task_select + * + * Behaves identically to the UNIX system select function, with the exception + * that its timeout value is controlled externally by calls to system_task_block_select + * and system_task_noblock_select. + */ + int system_task_select (width, readfds, writefds, exceptfds) + int width; + fd_set *readfds, *writefds, *exceptfds; + { + int retval = select(width,readfds,writefds,exceptfds,sc_timeout); + sc_timeout = (struct timeval *)NULL; + return retval; + } + + + *** 1.2.1.2 1991/11/11 21:46:07 --- scrt/cio.h 1991/11/13 19:57:23 *************** *** 45,50 **** --- 45,55 ---- extern int sc_feof(); + extern void system_task_noblock_select_on_SIGIO (); + extern void system_task_noblock_select (); + extern void system_task_block_select (); + extern int system_task_select (); + extern int sc_ferror(); extern int sc_clearerr(); *** 1.2.1.2 1991/11/11 22:11:34 --- scrt/heap.c 1992/11/30 16:24:34 *************** *** 59,65 **** #ifdef APOLLO extern sc_regs(); #endif ! #ifdef SUN3 extern sc_a2to5d2to7(); #endif --- 59,65 ---- #ifdef APOLLO extern sc_regs(); #endif ! #if defined(SUN3) || defined(NeXT) || defined(HP300) extern sc_a2to5d2to7(); #endif *************** *** 106,116 **** int sc_gcinfo; /* controls logging */ ! #ifndef SYSV static struct rusage gcru, /* resource consumption during collection */ startru, stopru; ! #endif static int sc_newlist; /* list of newly allocated pages */ --- 106,116 ---- int sc_gcinfo; /* controls logging */ ! #if !defined(SYSV) && !defined(hpux) static struct rusage gcru, /* resource consumption during collection */ startru, stopru; ! #endif /* !defined(SYSV) && !defined(hpux) */ static int sc_newlist; /* list of newly allocated pages */ *************** *** 151,157 **** if (tail == page) tail = 0; \ } ! #ifndef SYSV /* The following function converts a rusage structure into an 18 word Scheme vector composed of the same items. */ --- 151,157 ---- if (tail == page) tail = 0; \ } ! #if !defined(SYSV) && !defined(hpux) /* The following function converts a rusage structure into an 18 word Scheme vector composed of the same items. */ *************** *** 283,292 **** } #else #define getrusage(x,y) /* no operation */ #define updategcru() /* no operation */ - #endif /* SYSV-BSD dependency */ /* Errors detected during garbage collection are logged by the following procedure. If any errors occur, the program will abort after logging them. More than 30 errors will result in the program being aborted at --- 283,294 ---- } #else + #define getrusage(x,y) /* no operation */ #define updategcru() /* no operation */ + #endif /* !defined(SYSV) && !defined(hpux) */ + /* Errors detected during garbage collection are logged by the following procedure. If any errors occur, the program will abort after logging them. More than 30 errors will result in the program being aborted at *************** *** 505,511 **** } #endif SPARC ! #ifdef SUN3 /* The following code is used to read the stack pointer. The register number is passed in to fo