|
|
1.1 ! root 1: ; Tasteful Flavors -*- Mode: Lisp; Package: SI; Base:8 -*- ! 2: ! 3: ;; (c) copywrite 1982, Massachusetts Institute of Technology ! 4: ! 5: ;; This flavor system is derived from the original Lisp machine ! 6: ;; flavor system. As such its distribution may be restricted to ! 7: ;; Lisp machine software license holders. ! 8: ! 9: (environment-lmlisp (eval compile load) (files flavorm)) ! 10: ! 11: (setq |SCCS-flavors| "@(#) flavors.l 1.1 83/03/14 @(#)") ! 12: ! 13: (DECLARE (SPECIAL ERRPORT) ! 14: (MACROS T)) ! 15: ! 16: ; A flavor-name is a symbol which names a type of objects defined ! 17: ; by the combination of several flavors. The SI:FLAVOR ! 18: ; property is a data-structure (of type FLAVOR) defining the ! 19: ; nature of the flavor, as defined below. ! 20: ! 21: ; Flavors come in essentially three kinds. The first kind defines a class ! 22: ; of flavors, and provides the basic instance variables and methods for ! 23: ; that class. This kind typically includes only VANILLA-FLAVOR as a ! 24: ; component, and uses the :REQUIRED-INSTANCE-VARIABLES and ! 25: ; :REQUIRED-METHODS options. The second kind of flavor represents a ! 26: ; particular option that may be combined in (a "mix-in"). The third ! 27: ; kind of flavor is the kind that can usefully be instantiated; it is ! 28: ; a combination of one of the first kind and several of the second kind, ! 29: ; to achieve the behavior desired for a particular application. ! 30: ! 31: ; The following symbols are interesting to outsiders: ! 32: ; DEFFLAVOR - macro for defining a flavor ! 33: ; DEFMETHOD - macro for defining a method ! 34: ; DEFWRAPPER - macro for defining a flavor-wrapper ! 35: ; INSTANTIATE-FLAVOR - create an object of a specified flavor ! 36: ; MAKE-INSTANCE - easier to call version of INSTANTIATE-FLAVOR ! 37: ; COMPILE-FLAVOR-METHODS - macro which does the right thing in the compiler ! 38: ; RECOMPILE-FLAVOR - function to recompile a flavor and maybe any flavors ! 39: ; that depend on it. Usually this happens automatically. ! 40: ; FUNCALL-SELF - a macro which, assuming you are a flavor instance, will ! 41: ; call yourself without bothering about rebinding the ! 42: ; variables. Will do something totally random if SELF ! 43: ; isn't a flavor instance. ! 44: ; LEXPR-FUNCALL-SELF - LEXPR-FUNCALL version of above ! 45: ; *ALL-FLAVOR-NAMES* - list of all symbols which have been used as the ! 46: ; name of a flavor ! 47: ; *FLAVOR-COMPILATIONS* - list of all methods which had to be compiled ! 48: ; this is useful for finding flavors which weren't compiled ! 49: ; in qfasl files or which need to be recompiled to bring ! 50: ; them up to date. ! 51: ; *FLAVOR-COMPILE-TRACE* - if non-NIL, a FORMAT destination for messages about ! 52: ; recompilation of combined methods ! 53: ; FLAVOR-ALLOWS-INIT-KEYWORD-P - determine whether a certain flavor allows ! 54: ; a certain keyword in its init-plist. ! 55: ; FLAVOR-ALLOWED-INIT-KEYWORDS - returns all the init keywords a flavor ! 56: ; handles. ! 57: ! 58: ; Roads not taken: ! 59: ; o Changing the size of all extant instances of a flavor. ! 60: ; o Nothing to stop you from instantiating a flavor of the first or ! 61: ; second kind. In practice you will usually get an error if you try it. ! 62: ! 63: ; This macro is used to define a flavor. Use DEFMETHOD to define ! 64: ; methods (responses to messages sent to an instance of a flavor.) ! 65: (DEFMACRO DEFFLAVOR (NAME INSTANCE-VARIABLES COMPONENT-FLAVORS &REST OPTIONS) ! 66: ;INSTANCE-VARIABLES can be symbols, or lists of symbol and initialization. ! 67: ;COMPONENT-FLAVORS are searched from left to right for methods, ! 68: ; and contribute their instance variables. ! 69: ;OPTIONS are: ! 70: ; (:GETTABLE-INSTANCE-VARIABLES v1 v2...) - enables automatic generation of methods ! 71: ; for retrieving the values of those instance variables ! 72: ; :GETTABLE-INSTANCE-VARIABLES - (the atomic form) does it for all instance ! 73: ; variables local to this flavor (declared in this DEFFLAVOR). ! 74: ; (:SETTABLE-INSTANCE-VARIABLES v1 v2...) - enables automatic generation of methods ! 75: ; for changing the values of instance variables ! 76: ; The atomic form works too. ! 77: ; (:REQUIRED-INSTANCE-VARIABLES v1 v2...) - any flavor incorporating this ! 78: ; flavor and actually instantiated must have instance variables with ! 79: ; the specified names. This is used for defining general types of ! 80: ; flavors. ! 81: ; (:REQUIRED-METHODS m1 m2...) - any flavor incorporating this ! 82: ; flavor and actually instantiated must have methods for the specified ! 83: ; operations. This is used for defining general types of flavors. ! 84: ; (:REQUIRED-FLAVORS f1 f2...) - similar, for component flavors ! 85: ; rather than methods. ! 86: ; (:INITABLE-INSTANCE-VARIABLES v1 v2...) - these instance variables ! 87: ; may be initialized via the options to INSTANTIATE-FLAVOR. ! 88: ; The atomic form works too. ! 89: ; Settable instance variables are also INITABLE. ! 90: ; (:INIT-KEYWORDS k1 k2...) - specifies keywords for the :INIT operation ! 91: ; which are legal to give to this flavor. Just used for error checking. ! 92: ; (:DEFAULT-INIT-PLIST k1 v1 k2 v2...) - specifies defaults to be put ! 93: ; into the init-plist, if the keywords k1, k2, ... are not already ! 94: ; specified, when instantiating. The values v1, v2, ... get evaluated ! 95: ; when and if they are used. ! 96: ; (:DEFAULT-HANDLER function) - causes function to be called if a message ! 97: ; is sent for which there is no method. Defaults to a function which ! 98: ; gives an error. ! 99: ; (:INCLUDED-FLAVORS f1 f2...) - specifies flavors to be included in this ! 100: ; flavor. The difference between this and specifying them as components ! 101: ; is that included flavors go at the end, so they act as defaults. This ! 102: ; makes a difference when this flavor is depended on by other flavors. ! 103: ; :NO-VANILLA-FLAVOR - do not include VANILLA-FLAVOR. ! 104: ; Normally it is included automatically. This is for esoteric hacks. ! 105: ; (:ORDERED-INSTANCE-VARIABLES v1 v2...) - requires that in any instance, ! 106: ; instance variables with these names must exist and come first. This might ! 107: ; be for instance variable slots specially referenced by microcode. ! 108: ; The atomic form works too. ! 109: ; (:OUTSIDE-ACCESSIBLE-INSTANCE-VARIABLES v1 v2...) - defines defsubsts which ! 110: ; act like defstruct accessors for the variables; that is, using these with ! 111: ; an argument of an instance gets the value of that variable in that instance. ! 112: ; The name of the defsubst is the flavor-name, hyphen, the variable name. ! 113: ; If the instance variable is ordered, the accessor will know its index ! 114: ; in the instance and access it directly, otherwise it will call ! 115: ; SYMEVAL-IN-CLOSURE at run-time. ! 116: ; The atomic form works too. ! 117: ; (:ACCESSOR-PREFIX sym) - uses "sym" as the prefix on the names of the above ! 118: ; defsubsts instead of "flavor-". ! 119: ; (:SELECT-METHOD-ORDER m1 m2...) - specifies that the keywords m1, m2, ... are ! 120: ; are important and should have their methods first in the select-method ! 121: ; table for increased efficiency. ! 122: ; (:METHOD-COMBINATION (type order operation1 operation2...)...) ! 123: ; Specify ways of combining methods from different flavors. :DAEMON NIL is the ! 124: ; the default. order is usually :BASE-FLAVOR-FIRST or :BASE-FLAVOR-LAST, ! 125: ; but this depends on type. ! 126: ; (:DOCUMENTATION <args>...) ! 127: ; The list of args is simply put on the flavor's :DOCUMENTATION property. ! 128: ; The standard for this is that the arguments may include keyword symbols and ! 129: ; a documentation string. To be specified more later. ! 130: ; There may be more. ! 131: (LET ((COPIED-OPTIONS (COPYLIST OPTIONS))) ! 132: (DEFFLAVOR1 NAME INSTANCE-VARIABLES COMPONENT-FLAVORS COPIED-OPTIONS) ! 133: ;; The following is done to determine all the instance variables ! 134: ;; that need to be declared special. ! 135: (IF (NOT (NULL (GETD 'LISZT))) ! 136: (COMPOSE-FLAVOR-COMBINATION (GET-FLAVOR NAME))) ! 137: `(PROGN 'COMPILE ! 138: ;; Define flavor at load time. ! 139: ;; Must come before the compile-time COMPOSE-AUTOMATIC-METHODS, ! 140: ;; which puts methods in the QFASL file. ! 141: (EVAL-WHEN (LOAD) ! 142: (DEFFLAVOR1 ',NAME ',INSTANCE-VARIABLES ',COMPONENT-FLAVORS ! 143: ',COPIED-OPTIONS)) ! 144: ,@(COMPOSE-AUTOMATIC-METHODS (GET NAME 'FLAVOR)) ! 145: ;; Make any instance-variable accessor macros. ! 146: ,@(DO ((VS (DO ((OPTS OPTIONS (CDR OPTS))) ! 147: ((NULL OPTS) NIL) ! 148: (AND (LISTP (CAR OPTS)) ! 149: (EQ (CAAR OPTS) ':OUTSIDE-ACCESSIBLE-INSTANCE-VARIABLES) ! 150: (RETURN (CDAR OPTS))) ! 151: (AND (EQ (CAR OPTS) ':OUTSIDE-ACCESSIBLE-INSTANCE-VARIABLES) ! 152: (RETURN (MAPCAR #'(LAMBDA (X) (IF (ATOM X) X (CAR X))) ! 153: INSTANCE-VARIABLES)))) ! 154: (CDR VS)) ! 155: (PREFIX (OR (CADR (ASSQ ':ACCESSOR-PREFIX OPTIONS)) ! 156: (CONCAT NAME "-"))) ! 157: (ORDS (DO ((OPTS OPTIONS (CDR OPTS))) ! 158: ((NULL OPTS) NIL) ! 159: (AND (LISTP (CAR OPTS)) ! 160: (EQ (CAAR OPTS) ':ORDERED-INSTANCE-VARIABLES) ! 161: (RETURN (CDAR OPTS))) ! 162: (AND (EQ (CAR OPTS) ':ORDERED-INSTANCE-VARIABLES) ! 163: (RETURN (MAPCAR #'(LAMBDA (X) (IF (ATOM X) X (CAR X))) ! 164: INSTANCE-VARIABLES))))) ! 165: (RES NIL (CONS `(DEFSUBST ,(INTERN (CONCAT PREFIX (CAR VS))) ! 166: (,NAME) ! 167: ,(IF (MEMQ (CAR VS) ORDS) ! 168: ; SMH@EMS VVV `(VREF ,NAME ! 169: ; ,(+ 9 (* 3 (FIND-POSITION-IN-LIST ! 170: ; (CAR VS) ORDS)))) ! 171: `(INT:FCLOSURE-STACK-STUFF ! 172: (VREF ,NAME ,(+ 3 (FIND-POSITION-IN-LIST ! 173: (CAR VS) ORDS)))) ! 174: ; SMH@EMS ^^^ ! 175: `(SYMEVAL-IN-FCLOSURE ,NAME ',(CAR VS)))) ! 176: RES))) ! 177: ((NULL VS) RES)) ! 178: ',NAME))) ! 179: ! 180: (DEFMACRO DEFUN-METHOD (FSPEC FLAVOR-NAME ARGLIST &BODY BODY) ! 181: `(DEFUN ,FSPEC ,ARGLIST ! 182: (DECLARE (SPECIAL SELF .OWN-FLAVOR. ! 183: ,@(FLAVOR-ALL-INSTANCE-VARIABLES ! 184: (GET-FLAVOR FLAVOR-NAME)))) ! 185: . ,BODY)) ! 186: ! 187: (DEFMACRO INSTANCE-VARIABLE-BOUNDP (X) ! 188: `(BOUNDP ',X)) ! 189: ! 190: (DEFVAR *ALL-FLAVOR-NAMES* NIL) ;List of names of all flavors (mostly for editor) ! 191: ! 192: (DEFVAR *USE-OLD-COMBINED-METHODS* T) ! 193: ;;T means recycle old, NIL means generate new. ! 194: ;; This is an implicit argument to certain routines. ! 195: ! 196: (DEFVAR *FLAVOR-PENDING-DEPENDS* NIL) ;Used by DEFFLAVOR1 ! 197: ! 198: (DEFVAR *FLAVOR-COMPILATIONS* NIL) ;List of methods compiled ! 199: ! 200: (DEFVAR *FLAVOR-COMPILE-TRACE* NIL) ! 201: ! 202: (DEFSUBST INSTANCE-FLAVOR (INSTANCE) ! 203: (SYMEVAL-IN-FCLOSURE INSTANCE '.OWN-FLAVOR.)) ! 204: ! 205: (DEFSUBST INSTANCE-FUNCTION (INSTANCE) ! 206: (FCLOSURE-FUNCTION INSTANCE)) ! 207: ! 208: (DEFUN GET-FLAVOR (FLAVOR-OR-NAME &AUX TEMP) ! 209: (COND ((:TYPEP FLAVOR-OR-NAME 'FLAVOR) FLAVOR-OR-NAME) ! 210: ((SYMBOLP FLAVOR-OR-NAME) ! 211: (SETQ TEMP (GET FLAVOR-OR-NAME 'FLAVOR)) ! 212: (CHECK-ARG FLAVOR-OR-NAME (:TYPEP TEMP 'FLAVOR) ! 213: "the name of a flavor") ! 214: TEMP) ! 215: (T (CHECK-ARG FLAVOR-OR-NAME (:TYPEP TEMP 'FLAVOR) ! 216: "the name of a flavor")))) ! 217: ! 218: ;;(DEFSUBST INSTANCEP (X) ! 219: ;; (AND (FCLOSUREP X) (EQ (FCLOSURE-FUNCTION X) #'FLAVOR-DISPATCH))) ! 220: ! 221: (DEFUN INSTANCE-TYPEP (OB TYPE) ! 222: (IF (NULL TYPE) ! 223: (FLAVOR-NAME (INSTANCE-FLAVOR OB)) ! 224: (NOT (NULL (MEMQ TYPE (FLAVOR-DEPENDS-ON-ALL ! 225: (INSTANCE-FLAVOR OB))))))) ! 226: ! 227: ! 228: ;These properties are not discarded by redoing a DEFFLAVOR. ! 229: (DEFCONST DEFFLAVOR1-PRESERVED-PROPERTIES ! 230: '(ADDITIONAL-INSTANCE-VARIABLES ! 231: COMPILE-FLAVOR-METHODS ! 232: MAPPED-COMPONENT-FLAVORS ! 233: INSTANCE-VARIABLE-INITIALIZATIONS ! 234: ALL-INITABLE-INSTANCE-VARIABLES ! 235: REMAINING-DEFAULT-PLIST ! 236: REMAINING-INIT-KEYWORDS)) ! 237: ! 238: ;These are instance variables that don't belong to this flavor or its components ! 239: ;but can be accessed by methods of this flavor. ! 240: (DEFSUBST FLAVOR-ADDITIONAL-INSTANCE-VARIABLES (FLAVOR) ! 241: (GET (FLAVOR-PLIST FLAVOR) 'ADDITIONAL-INSTANCE-VARIABLES)) ! 242: ! 243: ;The next four are distillations of info taken from this flavor and its components, ! 244: ;used for instantiating this flavor. See COMPOSE-FLAVOR-INITIALIZATIONS. ! 245: (DEFSUBST FLAVOR-INSTANCE-VARIABLE-INITIALIZATIONS (FLAVOR) ! 246: (GET (FLAVOR-PLIST FLAVOR) 'INSTANCE-VARIABLE-INITIALIZATIONS)) ! 247: ! 248: (DEFSUBST FLAVOR-REMAINING-DEFAULT-PLIST (FLAVOR) ! 249: (GET (FLAVOR-PLIST FLAVOR) 'REMAINING-DEFAULT-PLIST)) ! 250: ! 251: (DEFSUBST FLAVOR-REMAINING-INIT-KEYWORDS (FLAVOR) ! 252: (GET (FLAVOR-PLIST FLAVOR) 'REMAINING-INIT-KEYWORDS)) ! 253: ! 254: (DEFSUBST FLAVOR-ALL-INITABLE-INSTANCE-VARIABLES (FLAVOR) ! 255: (GET (FLAVOR-PLIST FLAVOR) 'ALL-INITABLE-INSTANCE-VARIABLES)) ! 256: ! 257: (DEFUN (FLAVOR :NAMED-STRUCTURE-INVOKE) (OPERATION &OPTIONAL SELF &REST ARGS) ! 258: (SELECTQ OPERATION ! 259: (:WHICH-OPERATIONS '(:PRINT-SELF :DESCRIBE)) ! 260: (:PRINT-SELF ! 261: (SI:PRINTING-RANDOM-OBJECT (SELF (CAR ARGS)) ! 262: (FORMAT (CAR ARGS) "FLAVOR ~S" (FLAVOR-NAME SELF)))) ! 263: (:DESCRIBE (DESCRIBE-FLAVOR SELF)) ! 264: (OTHERWISE ! 265: (FERROR NIL "~S UNKNOWN OPERATION FOR FLAVOR" OPERATION)))) ! 266: ! 267: ;Format of flavor-method-table: ! 268: ; New format of a flavor-method-table entry is: ! 269: ; (message combination-type combination-order meth...) ! 270: ; A meth is: ! 271: ; (function-spec definition plist) ! 272: ; Thus the second element of a meth is actually a function-cell. ! 273: ; The meth's are stored in permanent-storage-area so that they will be compact. ! 274: ; [That might not be the best area, the select-methods, and component ! 275: ; lists, and instanc-variable lists, and which-operations's, are also there.] ! 276: ; A magic-list entry is: ! 277: ; (message combination-type combination-order (method-type function-spec...)...) ! 278: ; In the magic-list, there can be more than one method listed under a method-type, ! 279: ; the base flavor always comes first. The :COMBINED methods are elided from ! 280: ; the magic-list. ! 281: ; ! 282: ; Special method-types: ! 283: ; NIL - no type specified ! 284: ; :DEFAULT - like NIL but only taken if there are no type-NIL methods ! 285: ; :WRAPPER - wrappers are remembered this way ! 286: ; :COMBINED - a daemon-caller; the symbol has a COMBINED-METHOD-DERIVATION property ! 287: ; whose value is the complete method table entry from the magic-list. ! 288: ; The CDDDR is canonicalized; each contained list of method symbols is ! 289: ; of course ordered by the order in which flavors are combined (base ! 290: ; flavor first). Canonical order is alphabetical by method-type. ! 291: ; Non-special method-types: ! 292: ; :BEFORE, :AFTER - these are used by the default combination-type, :DAEMON ! 293: ; ! 294: ; Special hair for wrappers: changing a wrapper can invalidate the combined method ! 295: ; without changing anything in the flavor-method-table entry. Rather than having ! 296: ; it automatically recompile, which turns out to be a pain when the wrapper was ! 297: ; just reloaded or changed trivially, it will fail to recompile and you must use ! 298: ; RECOMPILE-FLAVOR with a 3rd argument of NIL. ! 299: ; ! 300: ; A combination-type of NIL means it has not been explicitly specified. ! 301: ! 302: ; Method-combination functions. Found on the SI:METHOD-COMBINATION property ! 303: ; of the combination-type. These are passed the flavor structure, and the ! 304: ; magic-list entry, and must return the function spec to use as the handler. ! 305: ; It should also define or compile thew definition for that function spec if nec. ! 306: ; This function interprets combination-type-arg, ! 307: ; which for many combination-types is either :BASE-FLAVOR-FIRST or :BASE-FLAVOR-LAST. ! 308: ! 309: ;This is an a-list from method type to function to write the code to go ! 310: ;in the combined method. Users can add to this. ! 311: (DEFCONST *SPECIALLY-COMBINED-METHOD-TYPES* ! 312: '((:WRAPPER PUT-WRAPPER-INTO-COMBINED-METHOD))) ! 313: ! 314: ;Definitions of a meth (the datum which stands for a method) ! 315: ! 316: (DEFSTRUCT (METH :LIST :CONC-NAME (:CONSTRUCTOR NIL)) ! 317: ;No constructor because defstruct doesn't let me specify the area ! 318: FUNCTION-SPEC ! 319: DEFINITION ! 320: (PLIST NIL)) ! 321: ! 322: ; If there is no definition, it contains DTP-NULL and a pointer to the meth ! 323: ! 324: ; Extract the method-type of a meth ! 325: (DEFMACRO METH-METHOD-TYPE (METH) ! 326: `(AND (CDDDR (METH-FUNCTION-SPEC ,METH)) ! 327: (THIRD (METH-FUNCTION-SPEC ,METH)))) ! 328: ! 329: ; Return a meth of specified type from a list of meth's. ! 330: (DEFUN METH-LOOKUP (METHOD-TYPE METH-LIST) ! 331: (LOOP FOR METH IN METH-LIST ! 332: WHEN (EQ (METH-METHOD-TYPE METH) METHOD-TYPE) ! 333: RETURN METH)) ! 334: ! 335: (DEFUN NULLIFY-METHOD-DEFINITION (METH) ! 336: (SETF (METH-DEFINITION METH) NIL)) ! 337: ! 338: (DEFUN METH-DEFINEDP (METH) ! 339: (NOT (NULL (METH-DEFINITION METH)))) ! 340: ! 341: ;Function to define or redefine a flavor (used by DEFFLAVOR macro). ! 342: ;Note that to ease initialization problems, the flavors depended upon need ! 343: ;not be defined yet. You will get an error the first time you try to create ! 344: ;an instance of this flavor if a flavor it depends on is still undefined. ! 345: ;When redefining a flavor, we reuse the same FLAVOR defstruct so that ! 346: ;old instances continue to get the latest methods, unless you change ! 347: ;something incompatibly, in which case you will get a warning. ! 348: (DEFUN DEFFLAVOR1 (FLAVOR-NAME INSTANCE-VARIABLES COMPONENT-FLAVORS OPTIONS ! 349: &AUX FFL ALREADY-EXISTS INSTV IDENTICAL-COMPONENTS ! 350: GETTABLE SETTABLE INITABLE OLD-DEFAULT-HANDLER ! 351: OLD-DEFAULT-INIT-PLIST OLD-LOCAL-IVS OLD-INITABLE-IVS ! 352: OLD-INIT-KWDS ! 353: INIT-KEYWORDS INCLUDES METH-COMB ! 354: (PL (LIST 'FLAVOR-PLIST))) ! 355: (COND ((NOT (MEMQ FLAVOR-NAME *ALL-FLAVOR-NAMES*)) ! 356: (PUSH FLAVOR-NAME *ALL-FLAVOR-NAMES*))) ! 357: ;; Analyze and error check the instance-variable and component-flavor lists ! 358: (SETQ INSTV (MAPCAR #'(LAMBDA (X) (IF (ATOM X) X (CAR X))) ! 359: INSTANCE-VARIABLES)) ! 360: (DOLIST (IV INSTV) ! 361: (IF (OR (NULL IV) (NOT (SYMBOLP IV))) ! 362: (FERROR () "~S, which is not a symbol, was specified as an instance variable" IV))) ! 363: (DOLIST (CF COMPONENT-FLAVORS) ! 364: (IF (OR (NULL CF) (NOT (SYMBOLP CF))) ! 365: (FERROR () "~S, which is not a symbol, was specified as a component flavor" CF))) ! 366: ;; Certain properties are inherited from the old property list, while ! 367: ;; others are generated afresh each time from the defflavor-options. ! 368: (COND ((SETQ ALREADY-EXISTS (GET FLAVOR-NAME 'FLAVOR)) ! 369: (DOLIST (PROP DEFFLAVOR1-PRESERVED-PROPERTIES) ! 370: (PUTPROP PL (GET (FLAVOR-PLIST ALREADY-EXISTS) PROP) ! 371: PROP)))) ! 372: ;; First, parse all the defflavor options into local variables so we can see ! 373: ;; whether the flavor is being redefined incompatibly. ! 374: (DO ((L OPTIONS (CDR L)) ! 375: (OPTION) (ARGS)) ! 376: ((NULL L)) ! 377: (IF (ATOM (CAR L)) ! 378: (SETQ OPTION (CAR L) ARGS NIL) ! 379: (SETQ OPTION (CAAR L) ARGS (CDAR L))) ! 380: (SELECTQ OPTION ! 381: (:GETTABLE-INSTANCE-VARIABLES ! 382: (VALIDATE-INSTANCE-VARIABLES-SPEC ARGS INSTV FLAVOR-NAME OPTION) ! 383: (SETQ GETTABLE (OR ARGS INSTV))) ! 384: (:SETTABLE-INSTANCE-VARIABLES ! 385: (VALIDATE-INSTANCE-VARIABLES-SPEC ARGS INSTV FLAVOR-NAME OPTION) ! 386: (SETQ SETTABLE (OR ARGS INSTV))) ! 387: ((:INITABLE-INSTANCE-VARIABLES :INITABLE-INSTANCE-VARIABLES) ! 388: (VALIDATE-INSTANCE-VARIABLES-SPEC ARGS INSTV FLAVOR-NAME OPTION) ! 389: (SETQ INITABLE (OR ARGS INSTV))) ! 390: (:SPECIAL-INSTANCE-VARIABLES) ; Ignored since all IVs are special ! 391: (:INIT-KEYWORDS ! 392: (SETQ INIT-KEYWORDS ARGS)) ! 393: (:INCLUDED-FLAVORS ! 394: (SETQ INCLUDES ARGS)) ! 395: (:NO-VANILLA-FLAVOR ! 396: (PUTPROP PL T OPTION)) ! 397: (:ORDERED-INSTANCE-VARIABLES ! 398: ;Don't validate. User may reasonably want to specify non-local instance ! 399: ;variables, and any bogus names here will get detected by COMPOSE-FLAVOR-COMBINATION ! 400: ;(VALIDATE-INSTANCE-VARIABLES-SPEC ARGS INSTV FLAVOR-NAME OPTION) ! 401: (PUTPROP PL (OR ARGS INSTV) ':ORDERED-INSTANCE-VARIABLES)) ! 402: (:OUTSIDE-ACCESSIBLE-INSTANCE-VARIABLES ! 403: (VALIDATE-INSTANCE-VARIABLES-SPEC ARGS INSTV FLAVOR-NAME OPTION) ! 404: (PUTPROP PL (OR ARGS INSTV) ':OUTSIDE-ACCESSIBLE-INSTANCE-VARIABLES)) ! 405: (:METHOD-COMBINATION ! 406: (SETQ METH-COMB ARGS)) ! 407: (:DEFAULT-HANDLER ! 408: (PUTPROP PL (CAR ARGS) OPTION)) ! 409: ((:REQUIRED-INSTANCE-VARIABLES :REQUIRED-METHODS :REQUIRED-FLAVORS :DOCUMENTATION ! 410: :DEFAULT-INIT-PLIST :SELECT-METHOD-ORDER :ACCESSOR-PREFIX) ! 411: (PUTPROP PL ARGS OPTION)) ! 412: (OTHERWISE (FERROR () "~S unknown option to DEFFLAVOR" OPTION)))) ! 413: ;; All settable instance variables should also be gettable and INITABLE. ! 414: (DOLIST (V SETTABLE) ! 415: (OR (MEMQ V GETTABLE) ! 416: (PUSH V GETTABLE)) ! 417: (OR (MEMQ V INITABLE) ! 418: (PUSH V INITABLE))) ! 419: ;; See whether there are any changes in component flavor structure from last time ! 420: (SETQ IDENTICAL-COMPONENTS ! 421: (AND ALREADY-EXISTS ! 422: (EQUAL COMPONENT-FLAVORS (FLAVOR-DEPENDS-ON ALREADY-EXISTS)) ! 423: (EQUAL INCLUDES (FLAVOR-INCLUDES ALREADY-EXISTS)) ! 424: (EQUAL (GET PL ':REQUIRED-FLAVORS) ! 425: (GET (FLAVOR-PLIST ALREADY-EXISTS) ':REQUIRED-FLAVORS)))) ! 426: (AND ALREADY-EXISTS ! 427: (SETQ OLD-DEFAULT-HANDLER (GET (FLAVOR-PLIST ALREADY-EXISTS) ! 428: ':DEFAULT-HANDLER) ! 429: OLD-DEFAULT-INIT-PLIST (GET (FLAVOR-PLIST ALREADY-EXISTS) ! 430: ':DEFAULT-INIT-PLIST) ! 431: OLD-LOCAL-IVS (FLAVOR-LOCAL-INSTANCE-VARIABLES ALREADY-EXISTS) ! 432: OLD-INITABLE-IVS (FLAVOR-INITABLE-INSTANCE-VARIABLES ALREADY-EXISTS) ! 433: OLD-INIT-KWDS (FLAVOR-INIT-KEYWORDS ALREADY-EXISTS))) ! 434: ;; If the flavor is being redefined, and the number or order of instance ! 435: ;; variables is being changed, and this flavor or any that depends on it ! 436: ;; has a select-method table (i.e. has probably been instantiated), give ! 437: ;; a warning and disconnect from the old FLAVOR defstruct so that old ! 438: ;; instances will retain the old information. The instance variables can ! 439: ;; get changed either locally or by rearrangement of the component flavors. ! 440: (AND ALREADY-EXISTS ! 441: (IF (AND (EQUAL (GET PL ':ORDERED-INSTANCE-VARIABLES) ! 442: (GET (FLAVOR-PLIST ALREADY-EXISTS) ! 443: ':ORDERED-INSTANCE-VARIABLES)) ! 444: (OR (EQUAL (FLAVOR-LOCAL-INSTANCE-VARIABLES ALREADY-EXISTS) ! 445: INSTANCE-VARIABLES) ! 446: (EQUAL (MAPCAR #'(LAMBDA (X) (IF (ATOM X) X (CAR X))) ! 447: (FLAVOR-LOCAL-INSTANCE-VARIABLES ALREADY-EXISTS)) ! 448: INSTV)) ! 449: (OR IDENTICAL-COMPONENTS ! 450: (EQUAL (FLAVOR-RELEVANT-COMPONENTS ALREADY-EXISTS ! 451: COMPONENT-FLAVORS INCLUDES) ! 452: (FLAVOR-RELEVANT-COMPONENTS ALREADY-EXISTS ! 453: (FLAVOR-DEPENDS-ON ALREADY-EXISTS) ! 454: (FLAVOR-INCLUDES ALREADY-EXISTS))))) ! 455: NIL ! 456: (SETQ ALREADY-EXISTS (PERFORM-FLAVOR-REDEFINITION FLAVOR-NAME)))) ! 457: ;; Make the information structure unless the flavor already exists. ! 458: (LET ((FL (OR ALREADY-EXISTS ! 459: (GET FLAVOR-NAME 'UNDEFINED-FLAVOR) ! 460: (MAKE-FLAVOR FLAVOR-NAME FLAVOR-NAME)))) ! 461: (SETF (FLAVOR-LOCAL-INSTANCE-VARIABLES FL) INSTANCE-VARIABLES) ! 462: (SETF (FLAVOR-DEPENDS-ON FL) COMPONENT-FLAVORS) ! 463: (SETF (FLAVOR-PLIST FL) PL) ! 464: (IF GETTABLE ! 465: (SETF (FLAVOR-GETTABLE-INSTANCE-VARIABLES FL) GETTABLE)) ! 466: (IF SETTABLE ! 467: (SETF (FLAVOR-SETTABLE-INSTANCE-VARIABLES FL) SETTABLE)) ! 468: (SETF (FLAVOR-INITABLE-INSTANCE-VARIABLES FL) ! 469: (LOOP FOR V IN INITABLE COLLECT (CONS (CORRESPONDING-KEYWORD V) V))) ! 470: (SETF (FLAVOR-INIT-KEYWORDS FL) INIT-KEYWORDS) ! 471: (SETF (FLAVOR-INCLUDES FL) INCLUDES) ! 472: ;; First remove old method-combination declarations, then add new ones ! 473: (DOLIST (MTE (FLAVOR-METHOD-TABLE FL)) ! 474: (COND ((LOOP FOR DECL IN METH-COMB NEVER (MEMQ (CAR MTE) (CDDR DECL))) ! 475: (SETF (SECOND MTE) NIL) ! 476: (SETF (THIRD MTE) NIL)))) ! 477: (DOLIST (DECL METH-COMB) ! 478: (LET ((TYPE (CAR DECL)) (ORDER (CADR DECL)) ELEM) ! 479: ;; Don't error-check TYPE now, its definition might not be loaded yet ! 480: (DOLIST (MSG (CDDR DECL)) ! 481: (OR (SETQ ELEM (ASSQ MSG (FLAVOR-METHOD-TABLE FL))) ! 482: (PUSH (SETQ ELEM (LIST* MSG NIL NIL NIL)) (FLAVOR-METHOD-TABLE FL))) ! 483: (SETF (SECOND ELEM) TYPE) ! 484: (SETF (THIRD ELEM) ORDER)))) ! 485: ;; Make this a depended-on-by of its depends-on, or remember to do it ! 486: ;; later in the case of depends-on's not yet defined. ! 487: (DOLIST (COMPONENT-FLAVOR COMPONENT-FLAVORS) ! 488: (COND ((SETQ FFL (GET COMPONENT-FLAVOR 'FLAVOR)) ! 489: (OR (MEMQ FLAVOR-NAME (FLAVOR-DEPENDED-ON-BY FFL)) ! 490: (PUSH FLAVOR-NAME (FLAVOR-DEPENDED-ON-BY FFL)))) ! 491: (T (PUSH (CONS COMPONENT-FLAVOR FLAVOR-NAME) ! 492: *FLAVOR-PENDING-DEPENDS*)))) ! 493: ;; Likewise for its includes ! 494: (DOLIST (INCLUDED-FLAVOR (FLAVOR-INCLUDES FL)) ! 495: (COND ((SETQ FFL (GET INCLUDED-FLAVOR 'FLAVOR)) ! 496: (OR (MEMQ FLAVOR-NAME (FLAVOR-DEPENDED-ON-BY FFL)) ! 497: (PUSH FLAVOR-NAME (FLAVOR-DEPENDED-ON-BY FFL)))) ! 498: (T (PUSH (CONS INCLUDED-FLAVOR FLAVOR-NAME) ! 499: *FLAVOR-PENDING-DEPENDS*)))) ! 500: ;; If someone depends on this flavor, which wasn't defined until now, ! 501: ;; link them up. If that flavor was flavor-composed, recompose it now. ! 502: (DOLIST (X *FLAVOR-PENDING-DEPENDS*) ! 503: (COND ((EQ (CAR X) FLAVOR-NAME) ! 504: (OR (MEMQ (CDR X) (FLAVOR-DEPENDED-ON-BY FL)) ! 505: (PUSH (CDR X) (FLAVOR-DEPENDED-ON-BY FL))) ! 506: (SETQ *FLAVOR-PENDING-DEPENDS* ! 507: (DELQ X *FLAVOR-PENDING-DEPENDS*))))) ! 508: (PUTPROP FLAVOR-NAME FL 'FLAVOR) ! 509: ;; Now, if the flavor was redefined in a way that changes the methods but ! 510: ;; doesn't invalidate old instances, we have to propagate some changes. ! 511: (IF (AND ALREADY-EXISTS ! 512: (NOT IDENTICAL-COMPONENTS)) ! 513: (PERFORM-FLAVOR-METHOD-ONLY-REDEFINITION FLAVOR-NAME)) ! 514: FLAVOR-NAME)) ! 515: ! 516: ;Check for typos in user-specified lists of instance variables. ! 517: ;This assumes that only locally-specified (not inherited) instance variables ! 518: ;may be mentioned in DEFFLAVOR declaration clauses. ! 519: (DEFUN VALIDATE-INSTANCE-VARIABLES-SPEC (VARS-SPECD VARS-ALLOWED FLAVOR-NAME ! 520: OPTION &AUX BAD) ! 521: (DOLIST (VAR VARS-SPECD) ! 522: (OR (MEMQ VAR VARS-ALLOWED) (PUSH VAR BAD))) ! 523: (COND (BAD (FORMAT ERRPORT "~&ERROR: Flavor ~S has misspelled :~A ~%~S" ! 524: FLAVOR-NAME OPTION (NREVERSE BAD))))) ! 525: ! 526: ;List of those components which affect the names, number, and ordering of the ! 527: ;instance variables. Don't worry about undefined components, by definition ! 528: ;they must be different from the already-existing flavor, so the right ! 529: ;thing will happen. (I wonder what that comment means? Undefined components ! 530: ;will not even appear in the list.) ! 531: (DEFUN FLAVOR-RELEVANT-COMPONENTS (FL COMPONENT-FLAVORS INCLUDED-FLAVORS) ! 532: (SETF (FLAVOR-DEPENDS-ON FL) COMPONENT-FLAVORS) ! 533: (SETF (FLAVOR-INCLUDES FL) INCLUDED-FLAVORS) ! 534: (DEL-IF-NOT #'(LAMBDA (FLAVOR) ;Splice out the uninteresting ones ! 535: (FLAVOR-LOCAL-INSTANCE-VARIABLES FLAVOR)) ! 536: (COMPOSE-FLAVOR-INCLUSION (FLAVOR-NAME FL) NIL))) ! 537: ! 538: ;; Now that default structs are vectors, and plain copy works for vectors, ! 539: ;; this has been removed and replaced by copy. - SMH@EMS ! 540: ;(DEFUN COPY-HUNK-CONTENTS (H1 H2) ! 541: ; (LOOP FOR I FROM 0 TO (1- (HUNKSIZE H2)) ! 542: ; DO (SETF (CXR I H2) (CXR I H1)))) ! 543: ! 544: ;Propagate things from an old flavor to a new one which we construct, ! 545: ;for compiling a file. ! 546: (DEFUN FLAVOR-REDEFINITION-FOR-COMPILATION (OLD-FLAVOR NEW-COMPONENTS-P) ! 547: NEW-COMPONENTS-P ! 548: (LET ((NEW-FLAVOR (MAKE-FLAVOR FLAVOR-NAME (FLAVOR-NAME OLD-FLAVOR)))) ! 549: ;(COPY-HUNK-CONTENTS OLD-FLAVOR NEW-FLAVOR) ; SMH@EMS ! 550: (SETQ NEW-FLAVOR (COPY OLD-FLAVOR)) ; Now works only if vector. ! 551: ;; Do copy any combined methods. If we have dependents also in this file ! 552: ;; and they have COMPILE-FLAVOR-METHODS in this file, ! 553: ;; they will want to see our combined methods in case they can use them. ! 554: (COPY-METHOD-TABLE OLD-FLAVOR NEW-FLAVOR NIL) ! 555: (SETF (FLAVOR-DEPENDS-ON-ALL NEW-FLAVOR) NIL) ;Will need to be flavor-composed again ! 556: ;; Cause an error if these are looked at before they are valid. ! 557: (SETF (FLAVOR-ALL-INSTANCE-VARIABLES NEW-FLAVOR) 'NOT-COMPUTED) ! 558: (SETF (FLAVOR-DEPENDED-ON-BY NEW-FLAVOR) 'COMPILATION) ! 559: (SETF (FLAVOR-METHOD-HASH-TABLE NEW-FLAVOR) NIL) ;Will need to be method-composed again ! 560: (SETF (FLAVOR-WHICH-OPERATIONS NEW-FLAVOR) NIL) ! 561: NEW-FLAVOR)) ! 562: ! 563: (DEFUN COPY-METHOD-TABLE (OLD-FLAVOR NEW-FLAVOR DISCARD-COMBINED-METHODS) ! 564: (LET ((L (COPYLIST (FLAVOR-METHOD-TABLE OLD-FLAVOR)))) ! 565: (DO ((TAIL L (CDR TAIL))) ! 566: ((NULL TAIL)) ! 567: ;; Copy the method-table element, including the list of METH's. ! 568: (SETF (CAR TAIL) (COPYLIST (CAR TAIL))) ! 569: (IF DISCARD-COMBINED-METHODS ! 570: ;; Flush from the copy all combined methods. ! 571: (DO ((TAIL2 (CDDDR (CAR TAIL)) (CDR TAIL2))) ! 572: ((NULL TAIL2)) ! 573: (AND (EQ (METH-METHOD-TYPE (CAR TAIL2)) ':COMBINED) ! 574: (SETF (CDDDAR TAIL) ! 575: (DELQ (CAR TAIL2) (CDDDAR TAIL)))))) ! 576: ;; Now copy each METH that we didn't delete. ! 577: ;; Copying a METH is not trivial because it can contain a DTP-NULL. ! 578: (DO ((TAIL2 (CDDDR (CAR TAIL)) (CDR TAIL2))) ! 579: ((NULL TAIL2)) ! 580: (LET ((NEW-METH (LIST (FIRST (CAR TAIL2)) ! 581: NIL ! 582: (COPYLIST (THIRD (CAR TAIL2)))))) ! 583: (IF (METH-DEFINEDP (CAR TAIL2)) ! 584: (SETF (METH-DEFINITION NEW-METH) (METH-DEFINITION (CAR TAIL2))) ! 585: (NULLIFY-METHOD-DEFINITION NEW-METH)) ! 586: (SETF (CAR TAIL2) NEW-METH)))) ! 587: (SETF (FLAVOR-METHOD-TABLE NEW-FLAVOR) L))) ! 588: ! 589: ;Record a flavor definition, during compiling a file. ! 590: ;Instead of setting the name's FLAVOR property, we put an entry on the ! 591: ;FLAVORS element in the FILE-LOCAL-DECLARATIONS, where COMPILATION-FLAVOR looks. ! 592: (DEFVAR FILE-LOCAL-DECLARATIONS ()) ! 593: ! 594: (DEFUN COMPILATION-DEFINE-FLAVOR (FLAVOR-NAME FL) ! 595: (LET ((FLL (ASSQ 'FLAVORS FILE-LOCAL-DECLARATIONS))) ! 596: (COND ((NULL FLL) ! 597: (PUSH (NCONS 'FLAVORS) FILE-LOCAL-DECLARATIONS) ! 598: (SETQ FLL (CAR FILE-LOCAL-DECLARATIONS)))) ! 599: (PUTPROP FLL FL FLAVOR-NAME))) ! 600: ! 601: ;Call here when a flavor has been changed in a way that is not compatible ! 602: ;with old instances of this flavor or its dependents. ! 603: ;Arranges for those old instances to keep the old flavor structures and ! 604: ;methods. Return new copy of the FLAVOR defstruct, and propagate to those ! 605: ;that depend on it. Note that we tell copy-method-table to discard our ! 606: ;combined methods. This is because they point to METHs in our method table, ! 607: ;so we must make new combined methods that point at our new method table. ! 608: (DEFUN PERFORM-FLAVOR-REDEFINITION (FLAVOR-NAME &AUX FL NFL) ! 609: (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) ! 610: (COND ((FLAVOR-METHOD-HASH-TABLE FL) ! 611: (SETQ NFL (MAKE-FLAVOR)) ! 612: ; (COPY-HUNK-CONTENTS FL NFL) ; SMH@EMS ! 613: (SETQ NFL (COPY FL)) ; Now works only if FL is a vector! ! 614: (COPY-METHOD-TABLE FL NFL T) ;Copy, but discard combined methods ! 615: (SETQ FL NFL) ! 616: (SETF (FLAVOR-PLIST FL) (COPYLIST (FLAVOR-PLIST FL))) ! 617: (PUTPROP FLAVOR-NAME FL 'FLAVOR) ! 618: (FORMAT ERRPORT "~&Flavor ~S changed incompatibly, old instances will not get the new version.~%" ! 619: FLAVOR-NAME)) ! 620: ;; Even if this flavor wasn't instantiated, ! 621: ;; probably some of its dependents were, ! 622: ;; and their hash tables and combined methods point to our method table. ! 623: (T (COPY-METHOD-TABLE FL FL T))) ! 624: (SETF (FLAVOR-DEPENDS-ON-ALL FL) NIL) ;Will need to be flavor-composed again ! 625: (SETF (FLAVOR-METHOD-HASH-TABLE FL) NIL) ;Will need to be method-composed again ! 626: (SETF (FLAVOR-WHICH-OPERATIONS FL) NIL) ! 627: (DOLIST (FN (FLAVOR-DEPENDED-ON-BY FL)) ! 628: (PERFORM-FLAVOR-REDEFINITION FN)) ! 629: FL) ! 630: ! 631: ;This one is when the old instances don't have to be discarded, but recomposition ! 632: ;does have to occur because something was changed in the order of flavor combination ! 633: (DEFUN PERFORM-FLAVOR-METHOD-ONLY-REDEFINITION (FLAVOR-NAME) ! 634: (LET ((FDEFINE-FILE-PATHNAME NIL)) ;Don't give warnings for combined methods ! 635: ;; Reverse the list so that this flavor comes first, followed by directest descendents. ! 636: (DOLIST (FN (REVERSE (FLAVOR-DEPENDED-ON-BY-ALL (GET FLAVOR-NAME 'FLAVOR) ! 637: (LIST FLAVOR-NAME)))) ! 638: (LET ((FL (GET FN 'FLAVOR))) ! 639: (IF (FLAVOR-DEPENDS-ON-ALL FL) (COMPOSE-FLAVOR-COMBINATION FL)) ! 640: (IF (FLAVOR-METHOD-HASH-TABLE FL) (COMPOSE-METHOD-COMBINATION FL)))))) ! 641: ! 642: (DEFUN DESCRIBE-FLAVOR (FLAVOR-NAME &AUX FL) ! 643: (SETQ FL (IF (SYMBOLP FLAVOR-NAME) (GET-FLAVOR FLAVOR-NAME) ! 644: FLAVOR-NAME)) ! 645: (CHECK-ARG FLAVOR-NAME (:TYPEP FL 'FLAVOR) ! 646: "a flavor or the name of one") ! 647: (FORMAT T "~&Flavor ~S directly depends on flavors: ~:[none~;~1G~{~S~^, ~}~]~%" ! 648: FLAVOR-NAME (FLAVOR-DEPENDS-ON FL)) ! 649: (AND (FLAVOR-INCLUDES FL) ! 650: (FORMAT T " and directly includes ~{~S~^, ~}~%" (FLAVOR-INCLUDES FL))) ! 651: (AND (FLAVOR-DEPENDED-ON-BY FL) ! 652: (FORMAT T " and is directly depended on by ~{~S~^, ~}~%" (FLAVOR-DEPENDED-ON-BY FL))) ! 653: (AND (FLAVOR-DEPENDS-ON-ALL FL) ;If this has been computed, show it ! 654: (FORMAT T " and directly or indirectly depends on ~{~S~^, ~}~%" ! 655: (FLAVOR-DEPENDS-ON-ALL FL))) ! 656: (AND (FLAVOR-METHOD-HASH-TABLE FL) ;If has been composed ! 657: (FORMAT T "Flavor ~S has instance variables ~:S~%" ! 658: FLAVOR-NAME (FLAVOR-ALL-INSTANCE-VARIABLES FL))) ! 659: (COND ((NOT (NULL (FLAVOR-METHOD-TABLE FL))) ! 660: (FORMAT T "Not counting inherited methods, the methods for ~S are:~%" ! 661: FLAVOR-NAME) ! 662: (DOLIST (M (FLAVOR-METHOD-TABLE FL)) ! 663: (FORMAT T " ") ! 664: (DO ((TPL (SUBSET 'METH-DEFINEDP (CDDDR M)) (CDR TPL))) ! 665: ((NULL TPL)) ! 666: (IF (METH-METHOD-TYPE (CAR TPL)) ! 667: (FORMAT T "~A " (METH-METHOD-TYPE (CAR TPL)))) ! 668: (FORMAT T "~A" (CAR M)) ! 669: (IF (CDR TPL) (PRINC ", "))) ! 670: ;; Print the method combination type if there is any. ! 671: (AND (CADR M) ! 672: (FORMAT T " :~A~@[ :~A~]" (CADR M) (CADDR M))) ! 673: (TERPRI)))) ! 674: (AND (FLAVOR-ALL-INSTANCE-VARIABLES FL) ! 675: (FORMAT T "Instance variables: ~{~S~^, ~}~%" (FLAVOR-ALL-INSTANCE-VARIABLES FL))) ! 676: (AND (FLAVOR-GETTABLE-INSTANCE-VARIABLES FL) ! 677: (FORMAT T "Automatically-generated methods to get instance variables: ~{~S~^, ~}~%" ! 678: (FLAVOR-GETTABLE-INSTANCE-VARIABLES FL))) ! 679: (AND (FLAVOR-SETTABLE-INSTANCE-VARIABLES FL) ! 680: (FORMAT T "Automatically-generated methods to set instance variables: ~{~S~^, ~}~%" ! 681: (FLAVOR-SETTABLE-INSTANCE-VARIABLES FL))) ! 682: (AND (FLAVOR-INITABLE-INSTANCE-VARIABLES FL) ! 683: (FORMAT T "Instance variables that may be set by initialization: ~{~S~^, ~}~%" ! 684: (MAPCAR #'CDR (FLAVOR-INITABLE-INSTANCE-VARIABLES FL)))) ! 685: (AND (FLAVOR-INIT-KEYWORDS FL) ! 686: (FORMAT T "Keywords in the :INIT message handled by this flavor: ~{~S~^, ~}~%" ! 687: (FLAVOR-INIT-KEYWORDS FL))) ! 688: (COND ((FLAVOR-PLIST FL) ! 689: (FORMAT T "Properties:~%") ! 690: (DO L (CDR (FLAVOR-PLIST FL)) (CDDR L) (NULL L) ! 691: (FORMAT T "~5X~S: ~S~%" (CAR L) (CADR L))))) ! 692: (COND ((NULL (FLAVOR-METHOD-HASH-TABLE FL)) ! 693: (FORMAT T "Flavor ~S does not yet have a method hash table~%" FLAVOR-NAME)) ! 694: (T (FORMAT T "Flavor ~S has method hash table:~%" FLAVOR-NAME) ! 695: (PRINT (FLAVOR-METHOD-HASH-TABLE FL))))) ! 696: ! 697: ;; This is the standard way of defining a method of a class, ! 698: ;; so that the code will be compiled. ! 699: ;; If in place of the lambda-list you have a symbol, and the body ! 700: ;; is null, that symbol is a function which stands in for the method. ! 701: (DEFMACRO DEFMETHOD (SPEC LAMBDA-LIST . BODY) ! 702: (LET ((CLASS-NAME (CAR SPEC)) ! 703: (FUNCTION-SPEC (CONS ':METHOD SPEC)) ! 704: FUNCTION-NAME) ! 705: (SETQ FUNCTION-NAME (METHOD-FUNCTION-NAME FUNCTION-SPEC)) ! 706: `(PROGN 'COMPILE ! 707: (EVAL-WHEN (COMPILE LOAD EVAL) ! 708: (FLAVOR-NOTICE-METHOD ',FUNCTION-SPEC)) ! 709: ;; At load-time, define the method function ! 710: ,(COND ((AND (SYMBOLP LAMBDA-LIST) (NOT (NULL LAMBDA-LIST)) ! 711: (NULL BODY)) ! 712: #-Franz `(FDEFINE ',FUNCTION-SPEC ',LAMBDA-LIST) ! 713: #+Franz `(DEFUN ,FUNCTION-NAME (OPERATION . ,LAMBDA-LIST) ! 714: (,lambda-list (operation . ,lambda-list)))) ! 715: ((GET CLASS-NAME 'FLAVOR) ! 716: `(DEFUN ,FUNCTION-NAME (OPERATION . ,LAMBDA-LIST) ! 717: (DECLARE (SPECIAL SELF .OWN-FLAVOR. ! 718: ,@(FLAVOR-ALL-INSTANCE-VARIABLES ! 719: (GET-FLAVOR CLASS-NAME)))) ! 720: . ,BODY)) ! 721: (T ;; The non-flavor class system ! 722: (FERROR () "Old Class system is not SUPPORTED"))) ! 723: ',FUNCTION-SPEC))) ! 724: ! 725: (DEFUN REMOVE-COLON (SYMBOL) ! 726: (IF (= (GETCHARN SYMBOL 1) #/:) ! 727: (CONCAT (SUBSTRING SYMBOL 2)) ! 728: SYMBOL)) ! 729: ! 730: ; This lets you specify code to be wrapped around the invocation of the ! 731: ; various methods for an operation. For example, ! 732: ; (DEFWRAPPER (FOO-FLAVOR :OPERATION) ((ARG1 ARG2) . BODY) ! 733: ; `(WITH-FOO-LOCKED (SELF) ! 734: ; (PRE-FROBULATE SELF ARG1 ARG2) ! 735: ; ,@BODY ! 736: ; (POST-FROBULATE SELF ARG2 ARG1))) ! 737: ;Note that the wrapper needs to be defined at both compile and run times ! 738: ;so that compiling combined methods as part of the qfasl file works. ! 739: ! 740: #+Franz ! 741: (defmacro destructuring-bind (template values . body) ! 742: `(let ((,template ,values)) . ,body)) ! 743: ! 744: (DEFMACRO DEFWRAPPER ! 745: ((FLAVOR-NAME OPERATION) (DEFMACRO-LAMBDA . GUTS) &BODY BODY) ! 746: (LET ((FUNCTION-SPEC `(:METHOD ,FLAVOR-NAME :WRAPPER ,OPERATION)) ! 747: function-name) ! 748: (setq function-name (method-function-name function-spec)) ! 749: `(PROGN ;; 'COMPILE ! 750: ;; Unfortunately, in Franz wrappers should not be compiled ! 751: ;; since the actual definition is needed by macrocall. ! 752: ;; Macrocall is clearly a crock! ! 753: ;; The following optimization could go away if defmacro were ! 754: ;; very smart. ! 755: ,(IF (AND (SYMBOLP DEFMACRO-LAMBDA) ! 756: (EQUAL DEFMACRO-LAMBDA 'IGNORE)) ! 757: `(DEFMACRO ,function-name (IGNORE . ,GUTS) . ,BODY) ! 758: `(DEFMACRO ,function-name (ARGLISTNAME . ,GUTS) ! 759: `(DESTRUCTURING-BIND ,',DEFMACRO-LAMBDA (CDR ,ARGLISTNAME) ! 760: ,,@BODY))) ! 761: (flavor-notice-method ',function-spec)))) ! 762: ! 763: ;This just exists to be called at compile-time from the DEFMETHOD macro, ! 764: ;so that any combined methods generated by COMPILE-FLAVOR-METHODS will ! 765: ;know that this method will be around at run time and should be called. ! 766: (DEFUN FLAVOR-NOTICE-METHOD (FUNCTION-SPEC) ! 767: (LET ((METH (FLAVOR-METHOD-ENTRY FUNCTION-SPEC NIL T))) ! 768: (COND ((NOT (EQ (METH-DEFINITION METH) ! 769: (METHOD-FUNCTION-NAME FUNCTION-SPEC))) ! 770: (SETF (METH-DEFINITION METH) (METHOD-FUNCTION-NAME FUNCTION-SPEC)) ! 771: (RECOMPILE-FLAVOR (SECOND FUNCTION-SPEC) ! 772: (CAR (LAST FUNCTION-SPEC))))))) ! 773: ! 774: (DEFUN METHOD-FUNCTION-NAME (FUNCTION-SPEC) ! 775: (LET ((FLAVOR (SECOND FUNCTION-SPEC)) ! 776: (METHOD-TYPE (THIRD FUNCTION-SPEC)) ! 777: (MESSAGE (FOURTH FUNCTION-SPEC))) ! 778: (IF (NULL (CDDDR FUNCTION-SPEC)) ! 779: (SETQ MESSAGE (THIRD FUNCTION-SPEC) METHOD-TYPE NIL)) ! 780: (IF (NULL METHOD-TYPE) ! 781: (INTERN (FORMAT () "~A-~A-method" FLAVOR (REMOVE-COLON MESSAGE))) ! 782: (INTERN ! 783: (FORMAT () "~A-~A-~A-method" ! 784: FLAVOR (REMOVE-COLON METHOD-TYPE) (REMOVE-COLON MESSAGE)))))) ! 785: ! 786: ;Find or create a method-table entry for the specified method. ! 787: ;DONT-CREATE is NIL if method is to be created if necessary. ! 788: ; The flavor is "created" too, as an UNDEFINED-FLAVOR property ! 789: ; of the flavor name, just to record any properties of methods. ! 790: ;COPY-FLAVOR-IF-UNDEFINED-METH says we are going to alter the METH ! 791: ;for compilation if it is not defined, so the flavor should be copied in that case. ! 792: (DEFUN FLAVOR-METHOD-ENTRY (FUNCTION-SPEC DONT-CREATE ! 793: &OPTIONAL COPY-FLAVOR-IF-UNDEFINED-METH) ! 794: ;; Huh? Unused! -SMH ! 795: (LET ((FLAVOR-NAME (SECOND FUNCTION-SPEC)) ! 796: (TYPE (THIRD FUNCTION-SPEC)) ! 797: (MESSAGE (FOURTH FUNCTION-SPEC))) ! 798: (IF (NULL MESSAGE) (SETQ MESSAGE TYPE TYPE NIL)) ;If no type ! 799: (IF (OR (NULL MESSAGE) (NEQ (FIRST FUNCTION-SPEC) ':METHOD) ! 800: (> (LENGTH FUNCTION-SPEC) 4) ! 801: (NOT (SYMBOLP FLAVOR-NAME)) (NOT (SYMBOLP TYPE)) ! 802: (NOT (SYMBOLP MESSAGE))) ! 803: (FERROR () "~S is not a valid function-spec" FUNCTION-SPEC)) ! 804: (LET* ((FL (OR (GET-FLAVOR FLAVOR-NAME) ! 805: (GET FLAVOR-NAME 'UNDEFINED-FLAVOR) ! 806: (AND (NOT DONT-CREATE) ! 807: (PUTPROP FLAVOR-NAME ! 808: (MAKE-FLAVOR FLAVOR-NAME FLAVOR-NAME) ! 809: 'UNDEFINED-FLAVOR)))) ! 810: (MTE (AND FL (ASSQ MESSAGE (FLAVOR-METHOD-TABLE FL)))) ! 811: (METH (METH-LOOKUP TYPE (CDDDR MTE)))) ! 812: (AND (NULL MTE) (NOT DONT-CREATE) ! 813: ;; Message not previously known about, put into table ! 814: FL ! 815: (PUSH (SETQ MTE (LIST* MESSAGE NIL NIL NIL)) (FLAVOR-METHOD-TABLE FL))) ! 816: ;; Message known, search for the type entry ! 817: (COND (METH) ;Known by flavor ! 818: (DONT-CREATE NIL) ;Not to be created ! 819: ((NULL FL) NIL) ;Create, but no flavor defined ! 820: (T ;; Type not known, create a new meth with an unbound definition cell ! 821: (LET ((METH (LIST FUNCTION-SPEC NIL NIL))) ! 822: (NULLIFY-METHOD-DEFINITION METH) ! 823: (PUSH METH (CDDDR MTE)) ! 824: METH)))))) ! 825: ! 826: ;;; See if a certain method exists in a flavor ! 827: (DEFUN FLAVOR-METHOD-EXISTS (FL TYPE OPERATION &AUX MTE) ! 828: (AND (SETQ MTE (ASSQ OPERATION (FLAVOR-METHOD-TABLE FL))) ! 829: (LET ((METH (METH-LOOKUP TYPE (CDDDR MTE)))) ! 830: (AND METH (METH-DEFINEDP METH))))) ! 831: ! 832: ;;; Forcibly remove a method definition from a flavor's method table ! 833: ;;; Syntax is identical to the beginning of a defmethod for the same method. ! 834: (DEFMACRO UNDEFMETHOD (SPEC) ! 835: `(FUNDEFINE '(:METHOD . ,SPEC))) ! 836: ! 837: ;Make an object of a particular flavor, taking the init-plist options ! 838: ;as a rest argument and sending the :INIT message if the flavor ! 839: ;handles it. ! 840: (DEFUN MAKE-INSTANCE (FLAVOR-NAME &REST INIT-OPTIONS) ! 841: (INSTANTIATE-FLAVOR FLAVOR-NAME (CONS 'INSTANCE-OPTIONS INIT-OPTIONS) ! 842: 'MAYBE)) ! 843: ! 844: (DEFUN FLAVOR-DISPATCH (MESSAGE &REST ARGUMENTS &AUX FUN) ! 845: (DECLARE (SPECIAL .OWN-FLAVOR.)) ! 846: (SETQ FUN (OR (GETHASH MESSAGE (FLAVOR-METHOD-HASH-TABLE .OWN-FLAVOR.)) ! 847: (FLAVOR-DEFAULT-HANDLER .OWN-FLAVOR.))) ! 848: (IF (NOT (NULL FUN)) ! 849: (LEXPR-FUNCALL FUN MESSAGE ARGUMENTS) ! 850: (FLAVOR-UNCLAIMED-MESSAGE MESSAGE ARGUMENTS))) ! 851: ! 852: ;; The first six slots are for SELF and .OWN-FLAVOR. The values are in the ! 853: ;; third slot. ! 854: ; SMH@EMS VVV ! 855: ; Perforce, %instance-ref no longer used. ! 856: ; (DEFSUBST %INSTANCE-REF (INSTANCE INDEX) ! 857: ; (VREF INSTANCE (+ 9. (* 3 INDEX)))) ! 858: ; (DEFSUBST INSTANCE-FLAVOR (INSTANCE) (VREF INSTANCE 6)) ! 859: ; The previous instance-flavor ought always to be good, if inefficient. ! 860: ; (DEFSUBST INSTANCE-FLAVOR (INSTANCE) (VREF INSTANCE 3)) ! 861: ; SMH@EMS ^^^ ! 862: ! 863: ;Make an object of a particular flavor. ! 864: ;If the flavor hasn't been composed yet, must do so now. ! 865: ; Delaying it until the first time it is needed aids initialization, ! 866: ; e.g. up until now we haven't depended on the depended-on flavors being defined yet. ! 867: ;Note that INIT-PLIST can be modified, if the :DEFAULT-INIT-PLIST option was ! 868: ; used or the init methods modify it. ! 869: (DEFUN INSTANTIATE-FLAVOR (FLAVOR-NAME INIT-PLIST ! 870: &OPTIONAL SEND-INIT-MESSAGE-P ! 871: RETURN-UNHANDLED-KEYWORDS-P ;as second value ! 872: &AUX FL FFL UNHANDLED-KEYWORDS INSTANCE VARS N TEM) ! 873: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) "the name of a flavor") ! 874: ;; Do any composition (compilation) of combined stuff, if not done already ! 875: (OR (FLAVOR-DEPENDS-ON-ALL FL) (COMPOSE-FLAVOR-COMBINATION FL)) ! 876: (OR (FLAVOR-METHOD-HASH-TABLE FL) (COMPOSE-METHOD-COMBINATION FL)) ! 877: (SETQ VARS (FLAVOR-ALL-INSTANCE-VARIABLES FL)) ! 878: ;; Make the instance object, then fill in its various fields ! 879: (SETQ INSTANCE ! 880: (PROGV `(SELF .OWN-FLAVOR. ,@VARS) ! 881: `(NIL ,FL) ! 882: (FCLOSURE `(SELF .OWN-FLAVOR. ,@VARS) ! 883: #'FLAVOR-DISPATCH))) ! 884: (LOOP FOR I FROM 0 TO (LENGTH VARS) ! 885: WITH IVS = (FLAVOR-INSTANCE-VARIABLE-INITIALIZATIONS FL) ! 886: WHEN (= I (CAAR IVS)) ! 887: ; SMH@EMS VVV ! 888: ; DO (PROGN (SETF (%INSTANCE-REF INSTANCE I) ! 889: ; (FAST-EVAL (CADAR IVS))) ! 890: ; (POP IVS))) ! 891: DO (PROGN (INT:FCLOSURE-STACK-STUFF (VREF INSTANCE (+ 3 I)) ! 892: (FAST-EVAL (CADAR IVS))) ! 893: (POP IVS))) ! 894: ; SMH@EMS ^^^ ! 895: (SET-IN-FCLOSURE INSTANCE 'SELF INSTANCE) ! 896: (LET ((VAR-KEYWORDS (FLAVOR-ALL-INITABLE-INSTANCE-VARIABLES FL)) ! 897: (REMAINING-KEYWORDS (FLAVOR-REMAINING-INIT-KEYWORDS FL))) ! 898: (COND (VAR-KEYWORDS ! 899: ;; First, process any user-specified init keywords that ! 900: ;; set instance variables. When we process the defaults, ! 901: ;; we will see that these are already set, and will ! 902: ;; refrain from evaluating the default forms. At the same time, ! 903: ;; we record any init keywords that this flavor doesn't handle. ! 904: (DO ((PL (CDR INIT-PLIST) (CDDR PL))) ((NULL PL)) ! 905: (COND ((MEMQ (CAR PL) VAR-KEYWORDS) ! 906: (SET-IN-FCLOSURE INSTANCE (REMOVE-COLON (CAR PL)) ! 907: (CADR PL))) ! 908: ((NOT (MEMQ (CAR PL) REMAINING-KEYWORDS)) ! 909: (PUSH (CAR PL) UNHANDLED-KEYWORDS)))) ! 910: ;; Now stick any default init plist items that aren't handled by ! 911: ;; that onto the actual init plist. ! 912: (DO ((PL (FLAVOR-REMAINING-DEFAULT-PLIST FL) (CDDR PL))) ! 913: ((NULL PL)) ! 914: (OR (MEMQ-ALTERNATED (CAR PL) (CDR INIT-PLIST)) ! 915: (PUTPROP INIT-PLIST (FAST-EVAL (CADR PL)) (CAR PL))))) ! 916: (T ! 917: ;; Put defaults into the INIT-PLIST ! 918: (FLAVOR-DEFAULT-INIT-PLIST FLAVOR-NAME INIT-PLIST) ! 919: ;; For each init keyword, either initialize the corresponding ! 920: ;; variable, remember that it will be handled later by an :INIT ! 921: ;; method, or give an error for not being handled. ! 922: (DO L (CDR INIT-PLIST) (CDDR L) (NULL L) ! 923: (LET ((KEYWORD (CAR L)) (ARG (CADR L))) ! 924: (DO ((FFLS (FLAVOR-DEPENDS-ON-ALL FL) (CDR FFLS))) ! 925: ((NULL FFLS) (PUSH KEYWORD UNHANDLED-KEYWORDS)) ! 926: (SETQ FFL (GET (CAR FFLS) 'FLAVOR)) ! 927: (COND ((SETQ TEM (ASSQ KEYWORD (FLAVOR-INITABLE-INSTANCE-VARIABLES FFL))) ! 928: (SET-IN-FCLOSURE INSTANCE (REMOVE-COLON KEYWORD) ! 929: ARG) ! 930: (RETURN)) ! 931: ((MEMQ KEYWORD (FLAVOR-INIT-KEYWORDS FFL)) ! 932: (RETURN))))))))) ! 933: ;; Complain if any keywords weren't handled, unless our caller ! 934: ;; said it wanted to take care of this. ! 935: (AND (NOT RETURN-UNHANDLED-KEYWORDS-P) ! 936: UNHANDLED-KEYWORDS ! 937: (FERROR () "Flavor ~S does not handle the init keyword~P ~{~S~^, ~}" ! 938: FLAVOR-NAME ! 939: (LENGTH UNHANDLED-KEYWORDS) ! 940: UNHANDLED-KEYWORDS)) ! 941: (AND (EQ SEND-INIT-MESSAGE-P 'MAYBE) ! 942: (NOT (GET-HANDLER-FOR INSTANCE ':INIT)) ! 943: (SETQ SEND-INIT-MESSAGE-P NIL)) ! 944: (AND SEND-INIT-MESSAGE-P ! 945: (SEND INSTANCE ':INIT INIT-PLIST)) ! 946: (VALUES INSTANCE UNHANDLED-KEYWORDS)) ! 947: ! 948: (DEFUN MEMQ-ALTERNATED (ELT LIST) ! 949: (DO ((L LIST (CDDR L))) ((NULL L) NIL) ! 950: (IF (EQ (CAR L) ELT) (RETURN L)))) ! 951: ! 952: (DEFUN FAST-EVAL (EXP) ! 953: (COND ((OR (NUMBERP EXP) (STRINGP EXP) ! 954: (MEMQ EXP '(T NIL))) ! 955: EXP) ! 956: ((SYMBOLP EXP) (SYMEVAL EXP)) ! 957: ((AND (LISTP EXP) (EQ (CAR EXP) 'QUOTE)) ! 958: (CADR EXP)) ! 959: (T (EVAL EXP)))) ! 960: ! 961: (DEFUN FLAVOR-DEFAULT-INIT-PLIST (FLAVOR-NAME ! 962: &OPTIONAL (INIT-PLIST (NCONS NIL)) ! 963: &AUX FL) ! 964: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) ! 965: "the name of a flavor") ! 966: ;; Do any composition (compilation) of combined stuff, if not done already ! 967: (OR (FLAVOR-DEPENDS-ON-ALL FL) (COMPOSE-FLAVOR-COMBINATION FL)) ! 968: (DOLIST (FFL (FLAVOR-DEPENDS-ON-ALL FL)) ! 969: (SETQ FFL (GET FFL 'FLAVOR)) ! 970: (DO L (GET (FLAVOR-PLIST FFL) ':DEFAULT-INIT-PLIST) (CDDR L) (NULL L) ! 971: (DO ((M (CDR INIT-PLIST) (CDDR M))) ! 972: ((NULL M) (PUTPROP INIT-PLIST (EVAL (CADR L)) (CAR L))) ! 973: (AND (EQ (CAR M) (CAR L)) (RETURN))))) ! 974: INIT-PLIST) ! 975: ! 976: ;Returns non-NIL if the flavor allows the specified keyword in its init-plist, ! 977: ;NIL if it doesn't. The return value is the name of the component flavor ! 978: ;that actually handles it. ! 979: (DEFUN FLAVOR-ALLOWS-INIT-KEYWORD-P (FLAVOR-NAME KEYWORD) ! 980: (MAP-OVER-COMPONENT-FLAVORS 0 T T ! 981: #'(LAMBDA (FL IGNORE KEYWORD) ! 982: (AND (OR (ASSQ KEYWORD (FLAVOR-INITABLE-INSTANCE-VARIABLES FL)) ! 983: (MEMQ KEYWORD (FLAVOR-INIT-KEYWORDS FL))) ! 984: (FLAVOR-NAME FL))) ! 985: FLAVOR-NAME NIL KEYWORD)) ! 986: ! 987: ;;; Given the name of a flavor, return a list of all of the symbols that ! 988: ;;; are valid init-options for the flavor, sorted alphabetically. ! 989: ;;; Primary for inquiries by humans. ! 990: (DEFUN FLAVOR-ALLOWED-INIT-KEYWORDS (FLAVOR-NAME) ! 991: (LET ((INIT-KEYWORDS NIL)) ! 992: (DECLARE (SPECIAL INIT-KEYWORDS)) ! 993: (MAP-OVER-COMPONENT-FLAVORS 0 T NIL ! 994: #'(LAMBDA (FLAVOR IGNORE) ! 995: (DECLARE (SPECIAL INIT-KEYWORDS)) ! 996: (SETQ INIT-KEYWORDS ! 997: (NCONC (MAPCAR #'(LAMBDA (KWD) ! 998: (IF (LISTP KWD) (CAR KWD) KWD)) ! 999: (FLAVOR-LOCAL-INIT-KEYWORDS FLAVOR)) ! 1000: INIT-KEYWORDS))) ! 1001: FLAVOR-NAME NIL) ! 1002: (SORT (ELIMINATE-DUPLICATES INIT-KEYWORDS) #'ALPHALESSP))) ! 1003: ! 1004: (DEFUN FLAVOR-LOCAL-INIT-KEYWORDS (FLAVOR) ! 1005: (APPEND (FLAVOR-INITABLE-INSTANCE-VARIABLES FLAVOR) ! 1006: (FLAVOR-INIT-KEYWORDS FLAVOR))) ! 1007: ! 1008: (DEFUN ELIMINATE-DUPLICATES (LIST &AUX L) ! 1009: (DOLIST (E LIST) (OR (MEMQ E L) (PUSH E L))) ! 1010: L) ! 1011: ! 1012: ; Function to map over all components of a specified flavor. We must do the ! 1013: ; DEPENDS-ON's to all levels first, then the INCLUDES's at all levels and ! 1014: ; what they depend on. ! 1015: ; Note that it does the specified flavor itself as well as all its components. ! 1016: ; Note well: if there are included flavors, this does not do them in the ! 1017: ; right order. Also note well: if there are multiple paths to a component, ! 1018: ; it will be done more than once. ! 1019: ; RECURSION-STATE is 0 except when recursively calling itself. ! 1020: ; ERROR-P is T if not-yet-defflavored flavors are to be complained about, ! 1021: ; NIL if they are to be ignored. This exists to get rid of certain ! 1022: ; bootstrapping problems. ! 1023: ; RETURN-FIRST-NON-NIL is T if the iteration should terminate as soon ! 1024: ; as FUNCTION returns a non-null result. ! 1025: ; At each stage FUNCTION is applied to the flavor (not the name), the ! 1026: ; STATE, and any ARGS. STATE is updated to whatever the function returns. ! 1027: ; The final STATE is the final result of this function. ! 1028: ; RECURSION-STATE is: ! 1029: ; 0 top-level ! 1030: ; 1 first-pass over just depends-on's ! 1031: ; 6 second-pass, this flavor reached via depends-on's so don't do it again ! 1032: ; 2 second-pass, this flavor reached via includes's so do it. ! 1033: (DEFVAR SOME-COMPONENT-UNDEFINED NIL) ;If we find an undefined component, we put its name here. ! 1034: ! 1035: (DEFUN MAP-OVER-COMPONENT-FLAVORS (RECURSION-STATE ERROR-P ! 1036: RETURN-FIRST-NON-NIL FUNCTION FLAVOR-NAME ! 1037: STATE &REST ARGS) ! 1038: (PROG (FL) ! 1039: (*CATCH 'MAP-OVER-COMPONENT-FLAVORS ! 1040: (COND ((OR ERROR-P (GET-FLAVOR FLAVOR-NAME)) ! 1041: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET-FLAVOR FLAVOR-NAME)) ! 1042: "a defined flavor") ! 1043: ;; First do this flavor, unless this is the second pass and it shouldn't be done ! 1044: (OR (BIT-TEST 4 RECURSION-STATE) ! 1045: (SETQ STATE (LEXPR-FUNCALL FUNCTION FL STATE ARGS))) ! 1046: ;; After each call to the function, see if we're supposed to be done now ! 1047: (AND RETURN-FIRST-NON-NIL (NOT (NULL STATE)) ! 1048: (*THROW 'MAP-OVER-COMPONENT-FLAVORS NIL)) ! 1049: ;; Now do the depends-on's. ! 1050: (DOLIST (COMPONENT-FLAVOR (FLAVOR-DEPENDS-ON FL)) ! 1051: (SETQ STATE (LEXPR-FUNCALL #'MAP-OVER-COMPONENT-FLAVORS ! 1052: (IF (ZEROP RECURSION-STATE) 1 RECURSION-STATE) ! 1053: ERROR-P RETURN-FIRST-NON-NIL ! 1054: FUNCTION COMPONENT-FLAVOR STATE ARGS)) ! 1055: (AND RETURN-FIRST-NON-NIL (NOT (NULL STATE)) ! 1056: (*THROW 'MAP-OVER-COMPONENT-FLAVORS NIL))) ! 1057: ;; Unless this is the first pass, do the includes. ! 1058: (OR (BIT-TEST 1 RECURSION-STATE) ! 1059: (DOLIST (COMPONENT-FLAVOR (FLAVOR-INCLUDES FL)) ! 1060: (SETQ STATE (LEXPR-FUNCALL #'MAP-OVER-COMPONENT-FLAVORS ! 1061: 2 ERROR-P RETURN-FIRST-NON-NIL ! 1062: FUNCTION COMPONENT-FLAVOR STATE ARGS)) ! 1063: (AND RETURN-FIRST-NON-NIL (NOT (NULL STATE)) ! 1064: (*THROW 'MAP-OVER-COMPONENT-FLAVORS NIL)))) ! 1065: ;; If this is the top-level, run the second pass on its depends-on's ! 1066: ;; which doesn't do them but does do what they include. ! 1067: (OR (NOT (ZEROP RECURSION-STATE)) ! 1068: (DOLIST (COMPONENT-FLAVOR (FLAVOR-DEPENDS-ON FL)) ! 1069: (SETQ STATE (LEXPR-FUNCALL #'MAP-OVER-COMPONENT-FLAVORS ! 1070: 6 ERROR-P RETURN-FIRST-NON-NIL ! 1071: FUNCTION COMPONENT-FLAVOR STATE ARGS)) ! 1072: (AND RETURN-FIRST-NON-NIL (NOT (NULL STATE)) ! 1073: (*THROW 'MAP-OVER-COMPONENT-FLAVORS NIL))))) ! 1074: ((NULL SOME-COMPONENT-UNDEFINED) ! 1075: (SETQ SOME-COMPONENT-UNDEFINED FLAVOR-NAME))))) ! 1076: STATE) ! 1077: ! 1078: ;Call this when a flavor has been changed, it updates that flavor's compiled ! 1079: ; information and that of any that depend on it. ! 1080: ;If a compilation is in progress the compilations performed ! 1081: ; will get output as part of that compilation. ! 1082: ;SINGLE-OPERATION is NIL to do all operations, or the name of an operation ! 1083: ; which needs incremental compilation. ! 1084: ;USE-OLD-COMBINED-METHODS can be NIL to force regeneration of all combined methods. ! 1085: ; This is used if a wrapper has changed or there was a bug in the method-combining routine. ! 1086: ;DO-DEPENDENTS controls whether flavors that depend on this one are also compiled. ! 1087: (DEFUN RECOMPILE-FLAVOR (FLAVOR-NAME ! 1088: &OPTIONAL (SINGLE-OPERATION NIL) (*USE-OLD-COMBINED-METHODS* T) ! 1089: (DO-DEPENDENTS T) ! 1090: &AUX FL) ! 1091: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) "the name of a flavor") ! 1092: ;; Only update the method combination if it has been done before, else ! 1093: ;; doesn't matter ! 1094: (COND ((FLAVOR-METHOD-HASH-TABLE FL) ! 1095: (OR (FLAVOR-DEPENDS-ON-ALL FL) ! 1096: (COMPOSE-FLAVOR-COMBINATION FL)) ! 1097: (COMPOSE-METHOD-COMBINATION FL SINGLE-OPERATION))) ! 1098: (IF DO-DEPENDENTS ! 1099: (LET ((FDEFINE-FILE-PATHNAME NIL)) ;Don't give warnings for combined methods ! 1100: (DOLIST (FN (FLAVOR-DEPENDED-ON-BY-ALL FL)) ! 1101: (IF (FLAVOR-METHOD-HASH-TABLE (GET FN 'FLAVOR)) ! 1102: (RECOMPILE-FLAVOR FN SINGLE-OPERATION *USE-OLD-COMBINED-METHODS* NIL)))))) ! 1103: ! 1104: ;Make a list of all flavors that depend on this one, not including this flavor itself. ! 1105: ;This is a list of the names, not the defstructs. ! 1106: (DEFUN FLAVOR-DEPENDED-ON-BY-ALL (FL &OPTIONAL (LIST-SO-FAR NIL) &AUX FFL) ! 1107: (DOLIST (FN (FLAVOR-DEPENDED-ON-BY FL)) ! 1108: (OR (MEMQ FN LIST-SO-FAR) ! 1109: (NOT (SETQ FFL (GET FN 'FLAVOR))) ! 1110: (SETQ LIST-SO-FAR (FLAVOR-DEPENDED-ON-BY-ALL FFL (CONS FN LIST-SO-FAR))))) ! 1111: LIST-SO-FAR) ! 1112: ! 1113: ;This function takes care of flavor-combination. It sets up the list ! 1114: ;of all component flavors, in appropriate order, and the list of all ! 1115: ;instance variables. It generally needs to be called only once for a ! 1116: ;flavor, and must be called before method-combination can be dealt with. ! 1117: (DEFVAR FLAVORS-BEING-COMPOSED NIL) ! 1118: ! 1119: (DEFUN COMPOSE-FLAVOR-COMBINATION (FL &AUX FLS VARS ORDS REQS SIZE ! 1120: (SOME-COMPONENT-UNDEFINED NIL) ! 1121: (FLAVORS-BEING-COMPOSED ! 1122: (CONS FL FLAVORS-BEING-COMPOSED))) ! 1123: ;; Make list of all component flavors' names. ! 1124: ;; This list is in outermost-first order. ! 1125: ;; Would be nice for this not to have to search to all levels, but for ! 1126: ;; the moment that is hard, so I won't do it. ! 1127: ;; Included-flavors are hairy: if not otherwise in the list of components, they ! 1128: ;; are stuck in after the rightmost component that includes them, along with ! 1129: ;; any components of their own not otherwise in the list. ! 1130: (SETQ FLS (COPYLIST (COMPOSE-FLAVOR-INCLUSION (FLAVOR-NAME FL) T))) ! 1131: ;; Don't mark this flavor as "composed" if there were errors. ! 1132: (OR SOME-COMPONENT-UNDEFINED ! 1133: (SETF (FLAVOR-DEPENDS-ON-ALL FL) FLS)) ! 1134: ;; Vanilla-flavor may have been put in by magic, so maintain the dependencies ! 1135: ;; in case new methods get added to it later. ! 1136: (LET ((VAN (GET-FLAVOR 'SI:VANILLA-FLAVOR)) ! 1137: (FLAV (FLAVOR-NAME FL))) ! 1138: (AND (NOT (NULL VAN)) ! 1139: (NEQ FLAV 'SI:VANILLA-FLAVOR) ! 1140: (MEMQ 'SI:VANILLA-FLAVOR FLS) ! 1141: (NOT (MEMQ FLAV (FLAVOR-DEPENDED-ON-BY VAN))) ! 1142: (PUSH FLAV (FLAVOR-DEPENDED-ON-BY VAN)))) ! 1143: ;; Compute what the instance variables will be, and in what order. ! 1144: ;; Also collect the required but not present instance variables, which go onto the ! 1145: ;; ADDITIONAL-INSTANCE-VARIABLES property. The instance variables of the ! 1146: ;; :REQUIRED-FLAVORS work the same way. Such instance variables are ok ! 1147: ;; for our methods to access. ! 1148: (DOLIST (F FLS) ! 1149: (SETQ F (GET-FLAVOR F)) ! 1150: (DOLIST (V (FLAVOR-LOCAL-INSTANCE-VARIABLES F)) ! 1151: (OR (ATOM V) (SETQ V (CAR V))) ! 1152: (OR (MEMQ V VARS) (PUSH V VARS))) ! 1153: (SETQ REQS (UNION REQS ! 1154: (GET (FLAVOR-PLIST F) ':REQUIRED-INSTANCE-VARIABLES))) ! 1155: ;; Any variables our required flavors have or require, we require. ! 1156: (DOLIST (FF (GET (FLAVOR-PLIST F) ':REQUIRED-FLAVORS)) ! 1157: (COND ((AND (NOT (MEMQ FF FLS)) ! 1158: (SETQ FF (GET-FLAVOR FF)) ! 1159: (NOT (MEMQ FF (CDR FLAVORS-BEING-COMPOSED)))) ! 1160: (OR (FLAVOR-DEPENDS-ON-ALL FF) (COMPOSE-FLAVOR-COMBINATION FF)) ! 1161: (SETQ REQS ! 1162: (UNION REQS (FLAVOR-ALL-INSTANCE-VARIABLES FF) ! 1163: (GET (FLAVOR-PLIST FF) 'ADDITIONAL-INSTANCE-VARIABLES)))))) ! 1164: (LET ((ORD (GET (FLAVOR-PLIST F) ':ORDERED-INSTANCE-VARIABLES))) ! 1165: ;; Merge into existing order requirement. Shorter of the two must be ! 1166: ;; a prefix of the longer, and we take the longer. ! 1167: (DO ((L1 ORD (CDR L1)) ! 1168: (L2 ORDS (CDR L2))) ! 1169: (NIL) ! 1170: (COND ((NULL L1) (RETURN NIL)) ! 1171: ((NULL L2) (RETURN (SETQ ORDS ORD))) ! 1172: ((NEQ (CAR L1) (CAR L2)) ! 1173: (FERROR () ":ORDERED-INSTANCE-VARIABLES conflict, ~S vs ~S" ! 1174: (CAR L1) (CAR L2))))))) ! 1175: ;; Must not merge this with the previous loop, ! 1176: ;; to avoid altering order of instance variables ! 1177: ;; if a DEFFLAVOR is redone. ! 1178: (DOLIST (F FLS) ! 1179: (SETQ F (GET-FLAVOR F))) ! 1180: ;; This NREVERSE makes it compatible with the old code. There is no other reason for it. ! 1181: (SETQ VARS (NREVERSE VARS)) ! 1182: ;; Apply ordering requirement by moving those variables to the front. ! 1183: (DOLIST (V ORDS) ! 1184: (OR (MEMQ V VARS) ! 1185: (FERROR () "Flavor ~S lacks instance variable ~S which has an order requirement" ! 1186: (FLAVOR-NAME FL) V)) ! 1187: (SETQ VARS (DELQ V VARS))) ! 1188: (SETQ VARS (APPEND ORDS VARS)) ! 1189: (SETF (FLAVOR-ALL-INSTANCE-VARIABLES FL) (COPYLIST VARS)) ! 1190: ;; If there are any instance variables required but not present, save them ! 1191: ;; so that they can be declared special in methods. ! 1192: (DOLIST (V VARS) ! 1193: (SETQ REQS (DELQ V REQS))) ! 1194: (AND REQS (PUTPROP (FLAVOR-PLIST FL) ! 1195: (COPYLIST REQS) ! 1196: 'ADDITIONAL-INSTANCE-VARIABLES)) ! 1197: NIL) ! 1198: ! 1199: (DEFUN COMPOSE-FLAVOR-INCLUSION (FLAVOR ERROR-P) ! 1200: (MULTIPLE-VALUE-BIND (FLS ADDITIONS) (COMPOSE-FLAVOR-INCLUSION-1 FLAVOR NIL ERROR-P) ! 1201: ;; The new additions may themselves imply more components ! 1202: (DO L ADDITIONS (CDR L) (NULL L) ! 1203: (LET ((MORE-FLS (COMPOSE-FLAVOR-INCLUSION-1 (CAR L) FLS ERROR-P))) ! 1204: (DOLIST (F MORE-FLS) ! 1205: ;; This hair inserts F before (after) the thing that indirectly included it ! 1206: ;; and then puts that next on ADDITIONS so it gets composed also ! 1207: (LET ((LL (MEMQ (CAR L) FLS))) ! 1208: (RPLACA (RPLACD LL (CONS (CAR LL) (CDR LL))) F) ! 1209: (RPLACD L (CONS F (CDR L))))))) ! 1210: ;; Now attach vanilla-flavor if desired ! 1211: (OR (LOOP FOR FLAVOR IN FLS ! 1212: THEREIS (GET (FLAVOR-PLIST (GET-FLAVOR FLAVOR)) ! 1213: ':NO-VANILLA-FLAVOR)) ! 1214: (PUSH 'SI:VANILLA-FLAVOR FLS)) ! 1215: (NREVERSE FLS))) ! 1216: ! 1217: (local-declare ((special other-components)) ! 1218: (DEFUN COMPOSE-FLAVOR-INCLUSION-1 (FLAVOR OTHER-COMPONENTS ERROR-P) ! 1219: ;; First, make a backwards list of all the normal (non-included) components ! 1220: (LET ((FLS (MAP-OVER-COMPONENT-FLAVORS 1 ERROR-P NIL ! 1221: #'(LAMBDA (FL LIST) ! 1222: (SETQ FL (FLAVOR-NAME FL)) ! 1223: (OR (MEMQ FL LIST) ! 1224: (MEMQ FL OTHER-COMPONENTS) ! 1225: (PUSH FL LIST)) ! 1226: LIST) ! 1227: FLAVOR NIL)) ! 1228: (ADDITIONS NIL)) ! 1229: ;; If there are any inclusions that aren't in the list, plug ! 1230: ;; them in right after (before in backwards list) their last (first) includer ! 1231: (DO L FLS (CDR L) (NULL L) ! 1232: (DOLIST (FL (FLAVOR-INCLUDES (GET-FLAVOR (CAR L)))) ! 1233: (OR (MEMQ FL FLS) ! 1234: (MEMQ FL OTHER-COMPONENTS) ! 1235: (PUSH (CAR (RPLACA (RPLACD L (CONS (CAR L) (CDR L))) FL)) ADDITIONS)))) ! 1236: (OR (MEMQ FLAVOR FLS) ! 1237: (SETQ FLS (NCONC FLS ! 1238: (NREVERSE ! 1239: (LOOP FOR FL IN (FLAVOR-INCLUDES (GET-FLAVOR FLAVOR)) ! 1240: UNLESS (OR (MEMQ FL FLS) (MEMQ FL OTHER-COMPONENTS)) ! 1241: COLLECT FL ! 1242: AND DO (PUSH FL ADDITIONS)))))) ! 1243: (VALUES FLS ADDITIONS)))) ! 1244: ! 1245: ;Once the flavor-combination stuff has been done, do the method-combination stuff. ! 1246: ;The above function usually only gets called once, but this function gets called ! 1247: ;when a new method is added. ! 1248: ;Specify SINGLE-OPERATION to do this for just one operation, for incremental update. ! 1249: ;NOTE WELL: If a meth is in the method-table at all, it is considered to be defined ! 1250: ; for purposes of compose-method-combination. Thus merely putprop'ing a method, ! 1251: ; or calling flavor-notice-method, will make the flavor think that method exists ! 1252: ; when it is next composed. This is necessary to make compile-flavor-methods work. ! 1253: ; (Putprop must create the meth because loading does putprop before fdefine.) ! 1254: (DEFUN COMPOSE-METHOD-COMBINATION (FL &OPTIONAL (SINGLE-OPERATION NIL) ! 1255: &AUX TEM MAGIC-LIST ORDER DEF HT ! 1256: MSG ELEM HANDLERS FFL PL) ! 1257: ;; If we are doing wholesale method composition, ! 1258: ;; compose the flavor bindings list also. ! 1259: ;; This way it is done often enough, but not at every defmethod. ! 1260: (IF (NOT SINGLE-OPERATION) ! 1261: (COMPOSE-FLAVOR-INITIALIZATIONS FL)) ! 1262: ;; Look through all the flavors depended upon and collect the following: ! 1263: ;; A list of all the operations handled and all the methods for each, called MAGIC-LIST. ! 1264: ;; The default handler for unknown operations. ! 1265: ;; The declared order of entries in the select-method alist. ! 1266: ;; Also generate any automatically-created methods not already present. ! 1267: ;; MAGIC-LIST is roughly the same format as the flavor-method-table, see its comments. ! 1268: ;; Each magic-list entry is (message comb-type comb-order (type function-spec...)...) ! 1269: (DO ((FFLS (FLAVOR-DEPENDS-ON-ALL FL) (CDR FFLS))) ! 1270: ((NULL FFLS)) ! 1271: (SETQ FFL (GET-FLAVOR (CAR FFLS)) ! 1272: PL (FLAVOR-PLIST FFL)) ! 1273: (COND ((NOT SINGLE-OPERATION) ! 1274: (AND (SETQ TEM (GET PL ':SELECT-METHOD-ORDER)) ! 1275: (SETQ ORDER (NCONC ORDER (COPYLIST TEM)))))) ! 1276: ;; Add data from flavor method-table to magic-list ! 1277: ;; But skip over combined methods, they are not relevant here ! 1278: (DOLIST (MTE (FLAVOR-METHOD-TABLE FFL)) ! 1279: (SETQ MSG (CAR MTE)) ! 1280: (COND ((OR (NOT SINGLE-OPERATION) (EQ MSG SINGLE-OPERATION)) ! 1281: ;; Well, we're supposed to concern ourselves with this operation ! 1282: (SETQ ELEM (ASSQ MSG MAGIC-LIST)) ;What we already know about it ! 1283: (COND ((DOLIST (METH (CDDDR MTE)) ! 1284: (OR (EQ (METH-METHOD-TYPE METH) ':COMBINED) ! 1285: (NOT (METH-DEFINEDP METH)) ! 1286: (RETURN T))) ! 1287: ;; OK, this flavor really contributes to handling this operation ! 1288: (OR ELEM (PUSH (SETQ ELEM (LIST* MSG NIL NIL NIL)) MAGIC-LIST)) ! 1289: ;; For each non-combined method for this operation, add it to the front ! 1290: ;; of the magic-list element, thus they are in base-flavor-first order. ! 1291: (DOLIST (METH (CDDDR MTE)) ! 1292: (LET ((TYPE (METH-METHOD-TYPE METH))) ! 1293: (COND ((EQ TYPE ':COMBINED)) ! 1294: ((NOT (METH-DEFINEDP METH))) ! 1295: ((NOT (SETQ TEM (ASSQ TYPE (CDDDR ELEM)))) ! 1296: (PUSH (LIST TYPE (METH-FUNCTION-SPEC METH)) (CDDDR ELEM))) ! 1297: ;; Don't let the same method get in twice (how could it?) ! 1298: ((NOT (MEMQ (METH-FUNCTION-SPEC METH) (CDR TEM))) ! 1299: (PUSH (METH-FUNCTION-SPEC METH) (CDR TEM)))))))) ! 1300: ;; Pick up method-combination declarations ! 1301: (AND (CADR MTE) (CADR ELEM) ;If both specify combination-type, check ! 1302: (OR (NEQ (CADR MTE) (CADR ELEM)) (NEQ (CADDR MTE) (CADDR ELEM))) ! 1303: (FERROR () ! 1304: "Method-combination mismatch ~S-~S vs. ~S-~S, check your DEFFLAVOR's" ! 1305: (CADR MTE) (CADDR MTE) (CADR ELEM) (CADDR ELEM))) ! 1306: (COND ((CADR MTE) ;Save combination-type when specified ! 1307: (OR ELEM (PUSH (SETQ ELEM (LIST* MSG NIL NIL NIL)) MAGIC-LIST)) ! 1308: (SETF (CADR ELEM) (CADR MTE)) ! 1309: (SETF (CADDR ELEM) (CADDR MTE)))) )))) ! 1310: ;; This NREVERSE tends to put base-flavor methods last ! 1311: (SETQ MAGIC-LIST (NREVERSE MAGIC-LIST)) ! 1312: ;; Re-order the magic-list according to any declared required order ! 1313: (DOLIST (MSG (NREVERSE ORDER)) ! 1314: (AND (SETQ TEM (ASSQ MSG MAGIC-LIST)) ! 1315: (SETQ MAGIC-LIST (CONS TEM (DELQ TEM MAGIC-LIST 1))))) ! 1316: ;; Map over the magic-list. For each entry call the appropriate ! 1317: ;; method-combining routine, which will return a function spec for ! 1318: ;; the handler to use for this operation. ! 1319: (DOLIST (MTE MAGIC-LIST) ! 1320: ;; Punt if there are no methods at all (just a method-combination declaration) ! 1321: (COND ((CDDDR MTE) ! 1322: ;; Process the :DEFAULT methods; if there are any untyped methods the ! 1323: ;; default methods go away, otherwise they become untyped methods. ! 1324: (AND (SETQ TEM (ASSQ ':DEFAULT (CDDDR MTE))) ! 1325: (IF (ASSQ NIL (CDDDR MTE)) ! 1326: (SETF (CDDDR MTE) (DELQ TEM (CDDDR MTE))) ! 1327: (RPLACA TEM NIL))) ! 1328: (OR (SETQ TEM (GET (OR (CADR MTE) ':DAEMON) 'METHOD-COMBINATION)) ! 1329: (FERROR () "~S unknown method combination type for ~S operation" ! 1330: (CADR MTE) (CAR MTE))) ! 1331: (PUSH (FUNCALL TEM FL MTE) HANDLERS)) ! 1332: (T (SETQ MAGIC-LIST (DELQ MTE MAGIC-LIST 1))))) ! 1333: ;; Get back into declared order. We now have a list of function specs for handlers. ! 1334: (SETQ HANDLERS (NREVERSE HANDLERS)) ! 1335: (COND (SINGLE-OPERATION ! 1336: ;; If doing SINGLE-OPERATION, put it into the hash table. ! 1337: ;; If the operation is becoming defined and wasn't, or vice versa, ! 1338: ;; must recompute the which-operations list. ! 1339: (OR (COND ((NULL HANDLERS) ;Deleting method ! 1340: (NOT (REMHASH SINGLE-OPERATION ! 1341: (FLAVOR-METHOD-HASH-TABLE FL)))) ! 1342: (T ! 1343: (MULTIPLE-VALUE-BIND (NIL PREVIOUSLY-PRESENT) ! 1344: (SWAPHASH SINGLE-OPERATION ! 1345: (SETQ DEF (METHOD-FUNCTION-NAME ! 1346: (CAR HANDLERS))) ! 1347: (FLAVOR-METHOD-HASH-TABLE FL)) ! 1348: PREVIOUSLY-PRESENT))) ! 1349: (SETF (FLAVOR-WHICH-OPERATIONS FL) NIL))) ! 1350: ;; Working on all operations at once. ! 1351: (T ! 1352: (SETQ HT (MAKE-HASH-TABLE ! 1353: ':SIZE (FIX (TIMES 1.5 (LENGTH MAGIC-LIST))))) ! 1354: ;; If flavor currently has no hash table, it can't hurt to set ! 1355: ;; it early ! 1356: (OR (FLAVOR-METHOD-HASH-TABLE FL) ! 1357: (SETF (FLAVOR-METHOD-HASH-TABLE FL) HT)) ! 1358: (DO ((HANDLERS HANDLERS (CDR HANDLERS)) ! 1359: (ML MAGIC-LIST (CDR ML))) ! 1360: ((NULL ML)) ! 1361: (PUTHASH (CAAR ML) (SETQ DEF (METHOD-FUNCTION-NAME (CAR HANDLERS))) ! 1362: HT) ! 1363: (SETF (FLAVOR-METHOD-HASH-TABLE FL) HT) ! 1364: (SETF (FLAVOR-WHICH-OPERATIONS FL) NIL)) ;This will have to be recomputed ! 1365: ;; Make sure that the required variables and methods are present. ! 1366: (VERIFY-REQUIRED-FLAVORS-METHODS-AND-IVARS FL MAGIC-LIST))) ! 1367: NIL) ! 1368: ! 1369: (DEFUN VERIFY-REQUIRED-FLAVORS-METHODS-AND-IVARS (FL MAGIC-LIST) ! 1370: (DO ((FFLS (FLAVOR-DEPENDS-ON-ALL FL) (CDR FFLS)) ! 1371: (MISSING-METHODS NIL) ! 1372: (MISSING-INSTANCE-VARIABLES NIL) ! 1373: (MISSING-FLAVORS NIL) ! 1374: (REQUIRING-FLAVOR-ALIST NIL)) ! 1375: ((NULL FFLS) ! 1376: (AND (OR MISSING-INSTANCE-VARIABLES MISSING-METHODS MISSING-FLAVORS) ! 1377: (FERROR () "Flavor ~S is missing ~ ! 1378: ~:[~2*~;instance variable~P ~{~S~^, ~} ~]~ ! 1379: ~:[~3*~;~:[~;and ~]method~P ~{~S~^, ~}~]~ ! 1380: ~:[~3*~;~:[~;and ~]component flavor~P ~{~S~^, ~}~] ! 1381: Requiring Flavor alist: ~S" ! 1382: (FLAVOR-NAME FL) ! 1383: MISSING-INSTANCE-VARIABLES ! 1384: (LENGTH MISSING-INSTANCE-VARIABLES) ! 1385: MISSING-INSTANCE-VARIABLES ! 1386: MISSING-METHODS ! 1387: MISSING-INSTANCE-VARIABLES ! 1388: (LENGTH MISSING-METHODS) ! 1389: MISSING-METHODS ! 1390: MISSING-FLAVORS ! 1391: (OR MISSING-INSTANCE-VARIABLES MISSING-METHODS) ! 1392: (LENGTH MISSING-FLAVORS) ! 1393: MISSING-FLAVORS ! 1394: REQUIRING-FLAVOR-ALIST))) ! 1395: (LET ((PL (FLAVOR-PLIST (GET (CAR FFLS) 'FLAVOR)))) ! 1396: (DOLIST (REQM (GET PL ':REQUIRED-METHODS)) ! 1397: (OR (ASSQ REQM MAGIC-LIST) ! 1398: (MEMQ REQM MISSING-METHODS) ! 1399: (PROGN (PUSH REQM MISSING-METHODS) ! 1400: (PUSH (CONS (FIRST FFLS) REQM) REQUIRING-FLAVOR-ALIST)))) ! 1401: (DOLIST (REQV (GET PL ':REQUIRED-INSTANCE-VARIABLES)) ! 1402: (OR (MEMQ REQV (FLAVOR-ALL-INSTANCE-VARIABLES FL)) ! 1403: (MEMQ REQV MISSING-INSTANCE-VARIABLES) ! 1404: (PROGN (PUSH REQV MISSING-INSTANCE-VARIABLES) ! 1405: (PUSH (CONS (FIRST FFLS) REQV) REQUIRING-FLAVOR-ALIST)))) ! 1406: (DOLIST (REQF (GET PL ':REQUIRED-FLAVORS)) ! 1407: (OR (MEMQ REQF (FLAVOR-DEPENDS-ON-ALL FL)) ! 1408: (MEMQ REQF MISSING-FLAVORS) ! 1409: (PROGN (PUSH REQF MISSING-FLAVORS) ! 1410: (PUSH (CONS (FIRST FFLS) REQF) REQUIRING-FLAVOR-ALIST))))))) ! 1411: ! 1412: ;This is the default handler for flavors. ! 1413: (DEFUN FLAVOR-UNCLAIMED-MESSAGE (MESSAGE ARGS) ! 1414: (DECLARE (SPECIAL SELF)) ! 1415: (FORMAT T "The object ") ! 1416: (PRINT SELF) ! 1417: (FERROR ':UNCLAIMED-MESSAGE " received a ~S message, which went unclaimed. ! 1418: The rest of the message was ~S~%" MESSAGE ARGS)) ! 1419: ! 1420: ;Return an alist of operations and their handlers. ! 1421: (DEFUN FLAVOR-METHOD-ALIST (FL) ! 1422: (IF (SYMBOLP FL) (SETQ FL (GET FL 'FLAVOR))) ! 1423: (IF FL ! 1424: (LET ((HT (FLAVOR-METHOD-HASH-TABLE FL)) ! 1425: (ALIST NIL)) ! 1426: (AND HT ! 1427: (MAPHASH #'(LAMBDA (OP METH-LOCATIVE &REST IGNORE) ! 1428: (DECLARE (SPECIAL ALIST)) ! 1429: (PUSH (CONS OP (CAR METH-LOCATIVE)) ALIST)) ! 1430: HT)) ! 1431: ALIST))) ! 1432: ! 1433: ;; Make the instance-variable getting and setting methods ! 1434: ;; Updated 7Jul84 SMH@MIT-EMS: As an apparent efficiency hack, the original ! 1435: ;; Lisp Machine code pushed each defmethod only if **just-compiling** were set ! 1436: ;; or the method were not yet defined. The **just-compiling** switch has ! 1437: ;; unfortunately disappeared from the Franz version. This caused ! 1438: ;; REcompilations of a flavor by a single instance of Liszt to omit all ! 1439: ;; automatic methods. The bypass of the defmethod if the method is already ! 1440: ;; defined has thus been deleted. ! 1441: (DEFUN COMPOSE-AUTOMATIC-METHODS (FL &AUX VV FORMS) ! 1442: (DOLIST (V (FLAVOR-GETTABLE-INSTANCE-VARIABLES FL)) ! 1443: (SETQ VV (CORRESPONDING-KEYWORD V)) ! 1444: (LET ((METH `(:METHOD ,(FLAVOR-NAME FL) ,VV))) ! 1445: (PUSH `(DEFMETHOD (,(FLAVOR-NAME FL) ,VV) () ,V) ! 1446: FORMS))) ! 1447: (DOLIST (V (FLAVOR-SETTABLE-INSTANCE-VARIABLES FL)) ! 1448: (SETQ VV (INTERN (FORMAT () ":set-~A" V))) ! 1449: (LET ((METH `(:METHOD ,(FLAVOR-NAME FL) ,VV))) ! 1450: (PUSH `(DEFMETHOD (,(FLAVOR-NAME FL) ,VV) (VALUE) ! 1451: (SETQ ,V VALUE)) ! 1452: FORMS))) ! 1453: (NREVERSE FORMS)) ! 1454: ! 1455: ;Given a symbol return the corresponding one in the keyword package ! 1456: (DEFUN CORRESPONDING-KEYWORD (SYMBOL) ! 1457: (IF (= #/: (GETCHARN SYMBOL 1)) SYMBOL ! 1458: (INTERN (CONCAT ":" SYMBOL)))) ! 1459: ! 1460: ;Figure out the information needed to instantiate a flavor quickly. ! 1461: ! 1462: ;We store these three properties on the flavor: ! 1463: ;INSTANCE-VARIABLE-INITIALIZATIONS - alist of (ivar-index . init-form) ! 1464: ;REMAINING-DEFAULT-PLIST - a default plist from which kwds that init ivars ! 1465: ; have been removed. ! 1466: ;ALL-INITABLE-INSTANCE-VARIABLES - ! 1467: ; a list parallel to FLAVOR-ALL-INSTANCE-VARIABLES which has either ! 1468: ; the keyword to init with or NIL. ! 1469: ;REMAINING-INIT-KEYWORDS - ! 1470: ; the init keywords that are handled and don't just init ivars. ! 1471: ! 1472: ;We also set up the FLAVOR-DEFAULT-HANDLER of the flavor. ! 1473: ! 1474: (DEFUN COMPOSE-FLAVOR-INITIALIZATIONS (FL &AUX ALIST ! 1475: (REMAINING-DEFAULT-PLIST (LIST NIL)) ! 1476: ALL-INITABLE-IVARS) ! 1477: (SETQ ALL-INITABLE-IVARS (MAKE-LIST ! 1478: (LENGTH (FLAVOR-ALL-INSTANCE-VARIABLES FL)))) ! 1479: ;; First make the mask saying which ivars can be inited by init keywords. ! 1480: (DOLIST (FFL (FLAVOR-DEPENDS-ON-ALL FL)) ! 1481: (LET ((FFL (GET-FLAVOR FFL))) ! 1482: (OR (FLAVOR-DEFAULT-HANDLER FL) ! 1483: (SETF (FLAVOR-DEFAULT-HANDLER FL) ! 1484: (GET (FLAVOR-PLIST FFL) ':DEFAULT-HANDLER))) ! 1485: (DOLIST (IIV (FLAVOR-INITABLE-INSTANCE-VARIABLES FFL)) ! 1486: (LET ((INDEX (FIND-POSITION-IN-LIST (CDR IIV) ! 1487: (FLAVOR-ALL-INSTANCE-VARIABLES FL)))) ! 1488: (AND INDEX ! 1489: (SETF (NTH INDEX ALL-INITABLE-IVARS) ! 1490: (CAR IIV))))))) ! 1491: ;; Then look at all the default init plists, for anything there that ! 1492: ;; initializes an instance variable. If it does, make an entry on ALIST. ! 1493: ;; Any that doesn't initialize a variable, put on the "remaining" list. ! 1494: (DOLIST (FFL (FLAVOR-DEPENDS-ON-ALL FL)) ! 1495: (SETQ FFL (GET-FLAVOR FFL)) ! 1496: (DO ((L (GET (FLAVOR-PLIST FFL) ':DEFAULT-INIT-PLIST) (CDDR L))) ((NULL L)) ! 1497: (LET* ((KEYWORD (CAR L)) (ARG (CADR L)) ! 1498: (INDEX (FIND-POSITION-IN-LIST KEYWORD ALL-INITABLE-IVARS))) ! 1499: (IF INDEX ! 1500: (OR (ASSQ INDEX ALIST) ! 1501: (PUSH (LIST INDEX ARG) ! 1502: ALIST)) ! 1503: ;; This keyword does not just initialize an instance variable. ! 1504: (OR (MEMQ-ALTERNATED KEYWORD (CDR REMAINING-DEFAULT-PLIST)) ! 1505: (PUTPROP REMAINING-DEFAULT-PLIST ARG KEYWORD)))))) ! 1506: ;; Then, look for default values provided in list of instance vars. ! 1507: (DOLIST (FFL (FLAVOR-DEPENDS-ON-ALL FL)) ! 1508: (SETQ FFL (GET-FLAVOR FFL)) ! 1509: (DOLIST (V (FLAVOR-LOCAL-INSTANCE-VARIABLES FFL)) ! 1510: (AND (NOT (ATOM V)) ! 1511: ;; When we find one, put it in if there is no init for that variable yet. ! 1512: (LET ((INDEX (FIND-POSITION-IN-LIST (CAR V) ! 1513: (FLAVOR-ALL-INSTANCE-VARIABLES FL)))) ! 1514: (AND (NOT (ASSQ INDEX ALIST)) ! 1515: (PUSH (LIST INDEX ! 1516: (CADR V)) ! 1517: ALIST)))))) ! 1518: (SETF (FLAVOR-INSTANCE-VARIABLE-INITIALIZATIONS FL) ! 1519: (SORTCAR ALIST #'LESSP)) ! 1520: (SETF (FLAVOR-REMAINING-DEFAULT-PLIST FL) (CDR REMAINING-DEFAULT-PLIST)) ! 1521: (SETF (FLAVOR-ALL-INITABLE-INSTANCE-VARIABLES FL) ALL-INITABLE-IVARS) ! 1522: (SETF (FLAVOR-REMAINING-INIT-KEYWORDS FL) ! 1523: (LOOP FOR K IN (FLAVOR-ALLOWED-INIT-KEYWORDS FL) ! 1524: UNLESS (MEMQ K ALL-INITABLE-IVARS) ! 1525: COLLECT K))) ! 1526: ! 1527: ; Method-combination functions. Found on the SI:METHOD-COMBINATION property ! 1528: ; of the combination-type. These are passed the flavor structure, and the ! 1529: ; magic-list entry, and must return the function-spec for the handler ! 1530: ; to go into the select-method, defining any necessary functions. ! 1531: ; This function interprets combination-type-arg, ! 1532: ; which for many combination-types is either :BASE-FLAVOR-FIRST or :BASE-FLAVOR-LAST. ! 1533: ! 1534: ; :DAEMON combination ! 1535: ; The primary method is the outermost untyped-method (or :DEFAULT). ! 1536: ; The :BEFORE methods are called base-flavor-last, the :AFTER methods are called ! 1537: ; base-flavor-first. An important optimization is not to generate a combined-method ! 1538: ; if there is only a primary method. You are allowed to omit the primary method ! 1539: ; if there are any daemons (I'm not convinced this is really a good idea) in which ! 1540: ; case the method's returned value will be NIL. ! 1541: (DEFUN (:DAEMON METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1542: (LET ((PRIMARY-METHOD (CAR (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL '(:BEFORE :AFTER) T ! 1543: ':BASE-FLAVOR-LAST))) ! 1544: (BEFORE-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':BEFORE T T ! 1545: ':BASE-FLAVOR-LAST)) ! 1546: (AFTER-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':AFTER T T ! 1547: ':BASE-FLAVOR-FIRST)) ! 1548: (WRAPPERS-P (SPECIALLY-COMBINED-METHODS-PRESENT MAGIC-LIST-ENTRY))) ! 1549: ;; Remove shadowed primary methods from the magic-list-entry so that it won't look like ! 1550: ;; we depend on them (which could cause extraneous combined-method recompilation). ! 1551: (LET ((MLE (ASSQ NIL (CDDDR MAGIC-LIST-ENTRY)))) ! 1552: (AND (CDDR MLE) ! 1553: (SETF (CDR MLE) (LIST PRIMARY-METHOD)))) ! 1554: (OR (AND (NOT WRAPPERS-P) (NULL BEFORE-METHODS) (NULL AFTER-METHODS) PRIMARY-METHOD) ! 1555: (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1556: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1557: (DAEMON-COMBINATION PRIMARY-METHOD BEFORE-METHODS AFTER-METHODS))))) ! 1558: ! 1559: (DEFUN DAEMON-COMBINATION (PRIMARY-METHOD BEFORE-METHODS AFTER-METHODS ! 1560: &OPTIONAL OR-METHODS AND-METHODS) ! 1561: (LET ((INNER-CALL (AND PRIMARY-METHOD (METHOD-CALL PRIMARY-METHOD)))) ! 1562: (IF (AND INNER-CALL AFTER-METHODS) ! 1563: (SETQ INNER-CALL `(MULTIPLE-VALUE (.VAL1. .VAL2. .VAL3.) ! 1564: ,INNER-CALL))) ! 1565: (AND OR-METHODS (SETQ INNER-CALL ! 1566: `(OR ,@(MAPCAR 'METHOD-CALL OR-METHODS) ! 1567: ,INNER-CALL))) ! 1568: (AND AND-METHODS (SETQ INNER-CALL ! 1569: `(AND ,@(MAPCAR 'METHOD-CALL AND-METHODS) ! 1570: ,INNER-CALL))) ! 1571: `(PROGN ! 1572: ,@(MAPCAR 'METHOD-CALL BEFORE-METHODS) ! 1573: ,(IF AFTER-METHODS ! 1574: ;; Kludge to return a few multiple values ! 1575: `(PROG (.VAL1. .VAL2. .VAL3.) ! 1576: ,INNER-CALL ! 1577: ,@(MAPCAR 'METHOD-CALL AFTER-METHODS) ! 1578: (RETURN .VAL1. .VAL2. .VAL3.)) ! 1579: ;; No :AFTER methods, hair not required ! 1580: ;; You are allowed to not have a primary method ! 1581: INNER-CALL)))) ! 1582: ! 1583: (DEFUN METHOD-CALL (METHOD) ! 1584: `(LEXPR-FUNCALL #',(METHOD-FUNCTION-NAME METHOD) .DAEMON-CALLER-ARGS.)) ! 1585: ! 1586: ; :DAEMON-WITH-OVERRIDE combination ! 1587: ; This is the same as :DAEMON (the default), except that :OVERRIDE type methods ! 1588: ; are combined with the :BEFORE-primary-:AFTER methods in an OR. This allows ! 1589: ; overriding of the main methods function. For example, a combined method as follows ! 1590: ; might be generated: (OR (FOO-OVERRIDE-BAR-METHOD) (PROGN (FOO-BEFORE-BAR-METHOD))) ! 1591: (DEFUN (:DAEMON-WITH-OVERRIDE METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1592: (LET ((PRIMARY-METHOD (CAR (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL ! 1593: '(:BEFORE :AFTER :OVERRIDE) T ! 1594: ':BASE-FLAVOR-LAST))) ! 1595: (BEFORE-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':BEFORE T T ! 1596: ':BASE-FLAVOR-LAST)) ! 1597: (AFTER-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':AFTER T T ! 1598: ':BASE-FLAVOR-FIRST)) ! 1599: (WRAPPERS-P (SPECIALLY-COMBINED-METHODS-PRESENT MAGIC-LIST-ENTRY)) ! 1600: (OVERRIDE-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ! 1601: ':OVERRIDE T T NIL))) ! 1602: ;; Remove shadowed primary methods from the magic-list-entry so that it won't look like ! 1603: ;; we depend on them (which could cause extraneous combined-method recompilation). ! 1604: (LET ((MLE (ASSQ NIL (CDDDR MAGIC-LIST-ENTRY)))) ! 1605: (AND (CDDR MLE) ! 1606: (SETF (CDR MLE) (LIST PRIMARY-METHOD)))) ! 1607: (OR (AND (NOT WRAPPERS-P) (NULL BEFORE-METHODS) (NULL AFTER-METHODS) ! 1608: (NULL OVERRIDE-METHODS) ! 1609: PRIMARY-METHOD) ! 1610: (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1611: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1612: `(OR ,@(MAPCAR 'METHOD-CALL OVERRIDE-METHODS) ! 1613: ,(DAEMON-COMBINATION PRIMARY-METHOD BEFORE-METHODS AFTER-METHODS)))))) ! 1614: ! 1615: ; :DAEMON-WITH-OR combination ! 1616: ; This is the same as :DAEMON (the default), except that :OR type methods ! 1617: ; are combined with the primary methods inside an OR, and used in place of ! 1618: ; the primary method in :DAEMON type combination. ! 1619: ; For example, the following combined method might be generated: ! 1620: ; (PROGN (FOO-BEFORE-BAR-METHOD) ! 1621: ; (PROG (.VAL1. .VAL2. .VAL3.) ! 1622: ; (OR (FOO-OR-BAR-METHOD) ! 1623: ; (BAZ-OR-BAR-METHOD) ! 1624: ; (MULTIPLE-VALUE (.VAL1. .VAL2. .VAL3.) ! 1625: ; (BUZZ-PRIMARY-METHOD))) ! 1626: ; (FOO-AFTER-BAR-METHOD) ! 1627: ; (RETURN .VAL1. .VAL2. .VAL3.))) ! 1628: ! 1629: (DEFUN (:DAEMON-WITH-OR METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1630: (LET ((PRIMARY-METHOD (CAR (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL '(:BEFORE :AFTER :OR) T ! 1631: ':BASE-FLAVOR-LAST))) ! 1632: (BEFORE-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':BEFORE T T ! 1633: ':BASE-FLAVOR-LAST)) ! 1634: (AFTER-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':AFTER T T ! 1635: ':BASE-FLAVOR-FIRST)) ! 1636: (WRAPPERS-P (SPECIALLY-COMBINED-METHODS-PRESENT MAGIC-LIST-ENTRY)) ! 1637: (OR-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':OR T T NIL))) ! 1638: ;; Remove shadowed primary methods from the magic-list-entry so that it won't look like ! 1639: ;; we depend on them (which could cause extraneous combined-method recompilation). ! 1640: (LET ((MLE (ASSQ NIL (CDDDR MAGIC-LIST-ENTRY)))) ! 1641: (AND (CDDR MLE) ! 1642: (SETF (CDR MLE) (LIST PRIMARY-METHOD)))) ! 1643: (OR (AND (NOT WRAPPERS-P) (NULL BEFORE-METHODS) (NULL AFTER-METHODS) ! 1644: (NULL OR-METHODS) ! 1645: PRIMARY-METHOD) ! 1646: (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1647: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1648: (DAEMON-COMBINATION PRIMARY-METHOD BEFORE-METHODS AFTER-METHODS ! 1649: OR-METHODS))))) ! 1650: ! 1651: ; :DAEMON-WITH-AND combination ! 1652: ; This is the same as :DAEMON (the default), except that :AND type methods ! 1653: ; are combined with the primary methods inside an AND, and used in place of ! 1654: ; the primary method in :DAEMON type combination. ! 1655: ; For example, the following combined method might be generated: ! 1656: ; (PROGN (FOO-BEFORE-BAR-METHOD) ! 1657: ; (PROG (.VAL1. .VAL2. .VAL3.) ! 1658: ; (AND (FOO-AND-BAR-METHOD) ! 1659: ; (BAZ-AND-BAR-METHOD) ! 1660: ; (MULTIPLE-VALUE (.VAL1. .VAL2. .VAL3.) ! 1661: ; (BUZZ-PRIMARY-METHOD))) ! 1662: ; (FOO-AFTER-BAR-METHOD) ! 1663: ; (RETURN .VAL1. .VAL2. .VAL3.))) ! 1664: ! 1665: (DEFUN (:DAEMON-WITH-AND METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1666: (LET ((PRIMARY-METHOD (CAR (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL '(:BEFORE :AFTER :AND) ! 1667: T ':BASE-FLAVOR-LAST))) ! 1668: (BEFORE-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':BEFORE T T ! 1669: ':BASE-FLAVOR-LAST)) ! 1670: (AFTER-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':AFTER T T ! 1671: ':BASE-FLAVOR-FIRST)) ! 1672: (WRAPPERS-P (SPECIALLY-COMBINED-METHODS-PRESENT MAGIC-LIST-ENTRY)) ! 1673: (AND-METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY ':AND T T NIL))) ! 1674: ;; Remove shadowed primary methods from the magic-list-entry so that it won't look like ! 1675: ;; we depend on them (which could cause extraneous combined-method recompilation). ! 1676: (LET ((MLE (ASSQ NIL (CDDDR MAGIC-LIST-ENTRY)))) ! 1677: (AND (CDDR MLE) ! 1678: (SETF (CDR MLE) (LIST PRIMARY-METHOD)))) ! 1679: (OR (AND (NOT WRAPPERS-P) (NULL BEFORE-METHODS) (NULL AFTER-METHODS) ! 1680: (NULL AND-METHODS) ! 1681: PRIMARY-METHOD) ! 1682: (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1683: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1684: (DAEMON-COMBINATION PRIMARY-METHOD BEFORE-METHODS AFTER-METHODS ! 1685: NIL AND-METHODS))))) ! 1686: ! 1687: ; :LIST combination ! 1688: ; No typed-methods allowed. Returns a list of the results of all the methods. ! 1689: ; There will always be a combined-method, even if only one method to be called. ! 1690: (DEFUN (:LIST METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1691: (OR (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1692: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1693: (CONS 'LIST (MAPCAR 'METHOD-CALL ! 1694: (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL NIL NIL NIL)))))) ! 1695: ! 1696: ; :INVERSE-LIST combination ! 1697: ; No typed-methods allowed. Apply each method to an element of the list. Given ! 1698: ; the result of a :LIST-combined method with the same ordering, and corresponding ! 1699: ; method definitions, the result that emerged from each component flavor gets handed ! 1700: ; back to that same flavor. The combined-method returns no particular value. ! 1701: (DEFUN (:INVERSE-LIST METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1702: (OR (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1703: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1704: `(LET ((.FOO. (CADR .DAEMON-CALLER-ARGS.))) ! 1705: . ,(DO ((ML (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL NIL NIL NIL) (CDR ML)) ! 1706: (R NIL)) ! 1707: ((NULL ML) (NREVERSE R)) ! 1708: (PUSH `(FUNCALL #',(CAR ML) ! 1709: (CAR .DAEMON-CALLER-ARGS.) (CAR .FOO.)) ! 1710: R) ! 1711: (AND (CDR ML) (PUSH '(SETQ .FOO. (CDR .FOO.)) R))))))) ! 1712: ! 1713: ; Combination types PROGN, AND, OR, MAX, MIN, +, APPEND, NCONC ! 1714: ; These just call all the untyped methods, inside the indicated special form. ! 1715: ; As an optimization, if there is only one method it is simply called. ! 1716: ; ?? There should be hair where methods with an extra keyword in them ! 1717: ; get to act as conditionals controlling which other methods get called, ! 1718: ; if anyone can ever specify exactly what this means. ! 1719: (DEFPROP :PROGN SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1720: (DEFPROP :AND SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1721: (DEFPROP :OR SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1722: (DEFPROP :MAX SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1723: (DEFPROP :MIN SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1724: (DEFPROP :+ SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1725: (DEFPROP :APPEND SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1726: (DEFPROP :NCONC SIMPLE-METHOD-COMBINATION METHOD-COMBINATION) ! 1727: ! 1728: ; The following "tasteless" crock is necessary to make all work in Franz: ! 1729: (eval-when (load eval) (loop for (to . from) in ! 1730: '((:progn . progn) ! 1731: (:and . and) ! 1732: (:or . or) ! 1733: (:max . max) ! 1734: (:min . min) ! 1735: (:+ . +) ! 1736: (:append . append) ! 1737: (:nconc . nconc)) ! 1738: do ! 1739: (putd to (getd from)))) ! 1740: ! 1741: (DEFUN SIMPLE-METHOD-COMBINATION (FL MAGIC-LIST-ENTRY) ! 1742: (LET ((METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL NIL NIL NIL)) ! 1743: (WRAPPERS-P (SPECIALLY-COMBINED-METHODS-PRESENT MAGIC-LIST-ENTRY))) ! 1744: (OR (AND (NOT WRAPPERS-P) (NULL (CDR METHODS)) (CAR METHODS)) ! 1745: (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1746: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1747: (CONS (CADR MAGIC-LIST-ENTRY) ! 1748: (MAPCAR 'METHOD-CALL ! 1749: METHODS)))))) ! 1750: ! 1751: ; :PASS-ON combination ! 1752: ; The values from the individual methods are the arguments to the next one; ! 1753: ; the values from the last method are the values returned by the combined ! 1754: ; method. Format is ! 1755: ; (:METHOD-COMBINATION (:PASS-ON (ORDERING . ARGLIST)) . OPERATION-NAMES) ! 1756: ; ORDERING is :BASE-FLAVOR-FIRST or :BASE-FLAVOR-LAST. ARGLIST can have ! 1757: ; &AUX and &OPTIONAL. ! 1758: ! 1759: (DEFUN (:PASS-ON METHOD-COMBINATION) (FL MAGIC-LIST-ENTRY) ! 1760: (LET ((METHODS (GET-CERTAIN-METHODS MAGIC-LIST-ENTRY NIL NIL NIL ! 1761: (CAADDR MAGIC-LIST-ENTRY))) ! 1762: (ARGLIST (CDADDR MAGIC-LIST-ENTRY)) ! 1763: ARGS REST-ARG-P) ! 1764: (DO ((L ARGLIST (CDR L)) ! 1765: (ARG) ! 1766: (NL NIL)) ! 1767: ((NULL L) ! 1768: (SETQ ARGS (NREVERSE NL))) ! 1769: (SETQ ARG (CAR L)) ! 1770: (AND (LISTP ARG) ! 1771: (SETQ ARG (CAR ARG))) ! 1772: (COND ((EQ ARG '&REST) ! 1773: (SETQ REST-ARG-P T)) ! 1774: ((EQ ARG '&AUX)) ! 1775: (T ! 1776: (PUSH ARG NL)))) ! 1777: (OR (HAVE-COMBINED-METHOD FL MAGIC-LIST-ENTRY) ! 1778: (MAKE-COMBINED-METHOD FL MAGIC-LIST-ENTRY ! 1779: `(DESTRUCTURING-BIND ,(CONS '.OPERATION. ARGLIST) SI:.DAEMON-CALLER-ARGS. ! 1780: . ,(DO ((METHS METHODS (CDR METHS)) ! 1781: (LIST NIL) ! 1782: (METH)) ! 1783: ((NULL METHS) ! 1784: (NREVERSE LIST)) ! 1785: (SETQ METH `(,(IF REST-ARG-P ! 1786: 'LEXPR-FUNCALL ! 1787: 'FUNCALL) ! 1788: #',(CAR METHS) .OPERATION. . ,ARGS)) ! 1789: (AND (CDR METHS) ! 1790: (SETQ METH (IF (NULL (CDR ARGS)) ! 1791: `(SETQ ,(CAR ARGS) ,METH) ! 1792: `(MULTIPLE-VALUE ,ARGS ,METH)))) ! 1793: (PUSH METH LIST))))))) ! 1794: ! 1795: ; This function does most of the analysis of the magic-list-entry needed by ! 1796: ; method-combination functions, including most error checking. ! 1797: ; Returns a list of the method symbols for METHOD-TYPE extracted from ! 1798: ; MAGIC-LIST-ENTRY. This value is shared with the data structure, don't ! 1799: ; bash it. OTHER-METHODS-ALLOWED is a list of method types not to complain ! 1800: ;about (T = allow all). ! 1801: ; NO-METHODS-OK = NIL means to complain if the returned value would be NIL. ! 1802: ; ORDERING-DECLARATION is :BASE-FLAVOR-FIRST, :BASE-FLAVOR-LAST, or NIL ! 1803: ; meaning take one of those symbols from the MAGIC-LIST-ENTRY." ! 1804: ! 1805: (DEFUN GET-CERTAIN-METHODS (MAGIC-LIST-ENTRY METHOD-TYPE OTHER-METHODS-ALLOWED ! 1806: NO-METHODS-OK ORDERING-DECLARATION ! 1807: &AUX (METHODS NIL)) ! 1808: ;; Find the methods of the desired type, and barf at any extraneous methods ! 1809: (DOLIST (X (CDDDR MAGIC-LIST-ENTRY)) ! 1810: (COND ((EQ (CAR X) METHOD-TYPE) (SETQ METHODS (CDR X))) ! 1811: ((ASSQ (CAR X) *SPECIALLY-COMBINED-METHOD-TYPES*) ) ;Wrappers ignored at this level ! 1812: ((OR (EQ OTHER-METHODS-ALLOWED T) (MEMQ (CAR X) OTHER-METHODS-ALLOWED)) ) ! 1813: (T (FERROR () "~S ~S method(s) illegal when using :~A method-combination" ! 1814: (CAR X) (CAR MAGIC-LIST-ENTRY) ! 1815: (OR (CADR MAGIC-LIST-ENTRY) ':DAEMON))))) ! 1816: ;; Complain if no methods supplied ! 1817: (AND (NULL METHODS) (NOT NO-METHODS-OK) ! 1818: (FERROR () "No ~S ~S method(s) supplied to :~A method-combination" ! 1819: METHOD-TYPE (CAR MAGIC-LIST-ENTRY) (CADR MAGIC-LIST-ENTRY))) ! 1820: ;; Get methods into proper order. Don't use NREVERSE! ! 1821: (SELECTQ (OR ORDERING-DECLARATION (SETQ ORDERING-DECLARATION (CADDR MAGIC-LIST-ENTRY))) ! 1822: (:BASE-FLAVOR-FIRST ) ! 1823: (:BASE-FLAVOR-LAST (SETQ METHODS (REVERSE METHODS))) ! 1824: (OTHERWISE (FERROR () "~S invalid method combination order; ! 1825: must be :BASE-FLAVOR-FIRST or :BASE-FLAVOR-LAST" ! 1826: ORDERING-DECLARATION))) ! 1827: METHODS) ! 1828: ! 1829: (DEFUN SPECIALLY-COMBINED-METHODS-PRESENT (MLE) ! 1830: (LOOP FOR (TYPE) IN (CDDDR MLE) ! 1831: THEREIS (ASSQ TYPE *SPECIALLY-COMBINED-METHOD-TYPES*))) ! 1832: ! 1833: ;; It is up to the caller to decide that a combined-method is called for at all. ! 1834: ;; If one is, this function decides whether it already exists OK or needs ! 1835: ;; to be recompiled. Returns the symbol for the combined method if it is ! 1836: ;; still valid, otherwise returns NIL. ! 1837: ;; Always canonicalizes the magic-list-entry, since it will be needed ! 1838: ;; canonicalized later. ! 1839: (DEFUN HAVE-COMBINED-METHOD (FL MAGIC-LIST-ENTRY ! 1840: &AUX OPERATION-NAME CMS MTE OLD-MLE OLD-CMS TEM OMETH) ! 1841: ;; Canonicalize the magic-list-entry so can compare with EQUAL ! 1842: (SETF (CDDDR MAGIC-LIST-ENTRY) ;Canonicalize before comparing ! 1843: (SORTCAR (CDDDR MAGIC-LIST-ENTRY) #'ALPHALESSP)) ;Sort by method-type ! 1844: (SETQ OPERATION-NAME (CAR MAGIC-LIST-ENTRY)) ! 1845: ;; See if we can inherit one in either the current or future (being-compiled) world, ! 1846: ;; or use an existing combined method of this flavor. ! 1847: ;; Get the :COMBINED method function spec for this flavor. Note that if a suitable ! 1848: ;; one can be inherited, we will do so. ! 1849: ;; *USE-OLD-COMBINED-METHODS* controls whether we reuse an existing one for this ! 1850: ;; flavor; if we inherit one it will always be up-to-date already. ! 1851: ;; If all OK, return the function spec, else return NIL if new combined method must be made. ! 1852: (OR (DOLIST (FFL (FLAVOR-DEPENDS-ON-ALL FL)) ! 1853: (LET ((FLAVOR1 (GET-FLAVOR FFL))) ! 1854: (AND (OR (NEQ FLAVOR1 FL) *USE-OLD-COMBINED-METHODS*) ! 1855: ;; ^ Combined methods of this flavor can be used only if permitted. ! 1856: (SETQ MTE (ASSQ OPERATION-NAME (FLAVOR-METHOD-TABLE FLAVOR1))) ! 1857: (SETQ OMETH (METH-LOOKUP ':COMBINED (CDDDR MTE))) ! 1858: (METH-DEFINEDP OMETH) ! 1859: (METH-DEFINITION OMETH) ! 1860: (SETQ CMS (METH-FUNCTION-SPEC OMETH)) ! 1861: (EQUAL MAGIC-LIST-ENTRY ! 1862: (SETQ TEM (GET (METH-PLIST OMETH) 'COMBINED-METHOD-DERIVATION))) ! 1863: (RETURN CMS))) ! 1864: ;Save first combined-method seen for tracing, it's the one we would ! 1865: ;have been most likely to inherit ! 1866: (OR OLD-CMS (NULL CMS) (NULL TEM) ! 1867: (SETQ OLD-CMS CMS OLD-MLE TEM))) ! 1868: ! 1869: ;; Have to make a new combined method. Trace if desired, but return NIL in any case. ! 1870: (PROGN ! 1871: (COND (*FLAVOR-COMPILE-TRACE* ! 1872: (FORMAT *FLAVOR-COMPILE-TRACE* ! 1873: "~&~S's ~S combined method needs to be recompiled~%to come from " ! 1874: (FLAVOR-NAME FL) OPERATION-NAME) ! 1875: (PRINT-COMBINED-METHOD-DERIVATION MAGIC-LIST-ENTRY *FLAVOR-COMPILE-TRACE*) ! 1876: (COND (OLD-CMS ! 1877: (FORMAT *FLAVOR-COMPILE-TRACE* ! 1878: "~%rather than using ~S which comes from " OLD-CMS) ! 1879: (PRINT-COMBINED-METHOD-DERIVATION OLD-MLE *FLAVOR-COMPILE-TRACE*)) ! 1880: ((NOT *USE-OLD-COMBINED-METHODS*) ! 1881: (FORMAT *FLAVOR-COMPILE-TRACE* "~%because of forced recompilation."))))) ! 1882: NIL))) ! 1883: ! 1884: ! 1885: (DEFUN PRINT-COMBINED-METHOD-DERIVATION (MLE STREAM) ! 1886: (LOOP FOR (TYPE . FUNCTION-SPECS) IN (CDDDR MLE) ! 1887: DO (LOOP FOR FUNCTION-SPEC IN FUNCTION-SPECS DO (FORMAT STREAM "~S " FUNCTION-SPEC))) ! 1888: (IF (OR (CADR MLE) (CADDR MLE)) ! 1889: (FORMAT STREAM "with method-combination ~S ~S" (CADR MLE) (CADDR MLE)))) ! 1890: ! 1891: ;; This function creates a combined-method, and returns the appropriate function spec. ! 1892: ;; Its main job in life is to take care of wrappers. Note the combined method ! 1893: ;; always takes a single &REST argument named .DAEMON-CALLER-ARGS. ! 1894: ;; FORM is a single form to be used as the body. ! 1895: (DEFUN MAKE-COMBINED-METHOD (FL MAGIC-LIST-ENTRY FORM &AUX FSPEC WRAPPERS) ! 1896: ;; Get the function spec which will name the combined-method ! 1897: (SETQ FSPEC `(:METHOD ,(FLAVOR-NAME FL) :COMBINED ,(CAR MAGIC-LIST-ENTRY))) ! 1898: ;; Put the wrappers around the form. The base-flavor wrapper goes on the inside. ! 1899: (SETQ WRAPPERS (GET-SPECIALLY-COMBINED-METHODS MAGIC-LIST-ENTRY FL)) ! 1900: (DOLIST (METHOD WRAPPERS) ! 1901: (SETQ FORM (FUNCALL (CADR (ASSQ (CADDR METHOD) *SPECIALLY-COMBINED-METHOD-TYPES*)) ! 1902: METHOD FORM))) ! 1903: ;; Remember that it's going to be there, for HAVE-COMBINED-METHOD ! 1904: (FLAVOR-NOTICE-METHOD FSPEC) ! 1905: ;; Compile the function. It will be inserted into the flavor's tables either ! 1906: ;; now or when the QFASL file is loaded. ! 1907: (COMPILE-AT-APPROPRIATE-TIME ! 1908: FL ! 1909: FSPEC ! 1910: `(LAMBDA (&REST .DAEMON-CALLER-ARGS.) ! 1911: .DAEMON-CALLER-ARGS. ! 1912: ,FORM) ! 1913: `(FUNCTION-SPEC-PUTPROP ',FSPEC ! 1914: ',MAGIC-LIST-ENTRY ! 1915: 'COMBINED-METHOD-DERIVATION)) ! 1916: FSPEC) ! 1917: ! 1918: ! 1919: (LOCAL-DECLARE ((SPECIAL *FL*)) ! 1920: (DEFUN GET-SPECIALLY-COMBINED-METHODS (MLE *FL*) ! 1921: (SORT (LOOP FOR (TYPE . FSPECS) IN (CDDDR MLE) ! 1922: WHEN (ASSQ TYPE *SPECIALLY-COMBINED-METHOD-TYPES*) ! 1923: APPEND FSPECS) ! 1924: #'(LAMBDA (FS1 FS2) ! 1925: (LOOP WITH FL1 = (CADR FS1) AND FL2 = (CADR FS2) ! 1926: FOR SUP IN (FLAVOR-DEPENDS-ON-ALL *FL*) ! 1927: WHEN (EQ SUP FL2) RETURN T ;Base flavor earlier in list ! 1928: WHEN (EQ SUP FL1) RETURN NIL))))) ! 1929: ! 1930: (DEFUN PUT-WRAPPER-INTO-COMBINED-METHOD (WRAPPER-NAME FORM) ! 1931: (LET ((DEF (COND #-Franz ((DECLARED-DEFINITION WRAPPER-NAME)) ! 1932: ;; What would the above mean in Franz? ! 1933: ((getd (method-function-name WRAPPER-NAME))) ! 1934: (T (FERROR () "~S supposed to be a wrapper macro, but missing!" ! 1935: WRAPPER-NAME))))) ! 1936: (COND ((not (and (dtpr DEF) ! 1937: (eq (CAR DEF) 'MACRO))) ! 1938: (FERROR () "~S, supposed to be a wrapper macro, is poorly formed. Definiton is ~s" ! 1939: WRAPPER-NAME DEF))) ! 1940: ;; Here we just put the wrapper in as a macro. It will be expanded by the compiler. ! 1941: `(MACROCALL ,WRAPPER-NAME .DAEMON-CALLER-ARGS. ,FORM))) ! 1942: ! 1943: ;Sort of a macro version of funcall, for wrappers ! 1944: (DEFMACRO MACROCALL (&REST X) ! 1945: (LET ((MACRO (COND #-Franz ((DECLARED-DEFINITION (CAR X))) ! 1946: ((method-function-name (CAR X))) ! 1947: (T (FERROR () "Unable to find definition of wrapper ~s at expand time" ! 1948: (CAR X)))))) ! 1949: (IF (AND (LISTP MACRO) (EQ (CAR MACRO) 'MACRO)) ! 1950: (FUNCALL (cons 'lambda (CDR MACRO)) X) ! 1951: ;--- Temporary code so I can test things in the kludge environment ! 1952: (IF (AND (SYMBOLP MACRO) (LISTP (getd MACRO)) ! 1953: (EQ (CAR (getd MACRO)) 'MACRO)) ! 1954: (FUNCALL (cons 'lambda (CDR (getd MACRO))) X) ! 1955: (FERROR () "~S evaluated to ~S, which is not a macro" ! 1956: (CAR X) MACRO))))) ! 1957: ! 1958: ;; Given a functional object, return its subfunction to do the given ! 1959: ;; operation or NIL. Returns NIL if it does not reduce to a select-method ! 1960: ;; or if it does not handle that." ! 1961: (DEFUN GET-HANDLER-FOR (FUNCTION OPERATION &OPTIONAL (SUPERIORS-P T) &AUX TEM) ! 1962: (COND ((SYMBOLP FUNCTION) ! 1963: (COND ((SETQ TEM (GET FUNCTION 'FLAVOR)) ! 1964: (GET-FLAVOR-HANDLER-FOR TEM OPERATION)))) ! 1965: ((:TYPEP FUNCTION 'FLAVOR) ! 1966: (GET-FLAVOR-HANDLER-FOR (FLAVOR-NAME FUNCTION) OPERATION)) ! 1967: ((INSTANCEP FUNCTION) ! 1968: ; SMH@EMS VVV ! 1969: ; (GET-FLAVOR-HANDLER-FOR (FLAVOR-NAME (VREF FUNCTION 6)) ! 1970: ; OPERATION) ! 1971: (GET-FLAVOR-HANDLER-FOR ! 1972: (FLAVOR-NAME (INT:FCLOSURE-STACK-STUFF (VREF FUNCTION 2))) ! 1973: OPERATION) ! 1974: ; SMH@EMS ^^^ ! 1975: ))) ! 1976: ! 1977: ;;; Get the function that would handle an operation for a flavor ! 1978: (DEFUN GET-FLAVOR-HANDLER-FOR (FLAVOR-NAME OPERATION &AUX FL) ! 1979: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) ! 1980: "the name of a flavor") ! 1981: ;; Do any composition (compilation) of combined stuff, if not done already ! 1982: (OR (FLAVOR-DEPENDS-ON-ALL FL) (COMPOSE-FLAVOR-COMBINATION FL)) ! 1983: (OR (FLAVOR-METHOD-HASH-TABLE FL) (COMPOSE-METHOD-COMBINATION FL)) ! 1984: (GETHASH OPERATION (FLAVOR-METHOD-HASH-TABLE FL))) ! 1985: ! 1986: (DEFUN SYMEVAL-IN-INSTANCE (INSTANCE VAR) ! 1987: (CHECK-ARG INSTANCE INSTANCEP "an instance") ! 1988: (SYMEVAL-IN-FCLOSURE INSTANCE VAR)) ! 1989: ! 1990: (DEFSETF SYMEVAL-IN-INSTANCE (E V) `(SET-IN-INSTANCE ,(CADR E) ,(CADDR E) ,V)) ! 1991: ! 1992: (DEFUN SET-IN-INSTANCE (INSTANCE VAR VAL) ! 1993: (CHECK-ARG INSTANCE INSTANCEP "an instance") ! 1994: (SET-IN-FCLOSURE INSTANCE VAR VAL)) ! 1995: ! 1996: ;Interface to the compiler. ! 1997: (DEFUN COMPILE-AT-APPROPRIATE-TIME (FL NAME LAMBDA-EXP &OPTIONAL FORM-TO-EVAL) ! 1998: (PUTD (METHOD-FUNCTION-NAME NAME) ! 1999: (LAMBDACVT (CDR LAMBDA-EXP)))) ! 2000: ! 2001: ;This macro takes flavor names as "arguments". It causes the compiler ! 2002: ;to include the appropriate methods in the qfasl file, provided all the ! 2003: ;component flavors are defined. ! 2004: (DEFMACRO COMPILE-FLAVOR-METHODS (&REST FLAVOR-NAMES) ! 2005: `(PROGN 'COMPILE ! 2006: (EVAL-WHEN (COMPILE) ! 2007: . ,(MAPCAN #'(LAMBDA (FLAVOR-NAME) ! 2008: (NCONC (AND (GET FLAVOR-NAME 'FLAVOR) ! 2009: (NCONS `(PUTPROP (FLAVOR-PLIST ! 2010: (GET ',FLAVOR-NAME 'FLAVOR)) ! 2011: T ! 2012: 'COMPILE-FLAVOR-METHODS))) ! 2013: (NCONS `(COMPILE-FLAVOR-METHODS-1 ',FLAVOR-NAME)))) ! 2014: FLAVOR-NAMES)) ! 2015: (EVAL-WHEN (LOAD EVAL) ! 2016: . ,(MAPCAR #'(LAMBDA (FLAVOR-NAME) `(COMPILE-FLAVOR-METHODS-2 ',FLAVOR-NAME)) ! 2017: FLAVOR-NAMES)))) ! 2018: ! 2019: ;; Cause the combined-methods to get compiled. ! 2020: ;; Executed only from the compiler, and does something ! 2021: ;; only if compiling to a file. ! 2022: (DEFUN COMPILE-FLAVOR-METHODS-1 (FLAVOR-NAME &AUX FL) ! 2023: (IF (JUST-COMPILING) ! 2024: (LET ((*JUST-COMPILING* T) ! 2025: (*USE-OLD-COMBINED-METHODS* NIL)) ! 2026: (COND ((FLAVOR-COMPONENTS-DEFINED-P FLAVOR-NAME 'COMPILE-FLAVOR-METHODS) ! 2027: (SETQ FL (GET-FLAVOR FLAVOR-NAME)) ! 2028: ;; Make sure we are not hacking the installed flavor object, ! 2029: ;; in case there is no defflavor or defmethod for the flavor in this file. ! 2030: (AND (EQ FL (GET FLAVOR-NAME 'FLAVOR)) ! 2031: (COMPILATION-DEFINE-FLAVOR ! 2032: FLAVOR-NAME ! 2033: (SETQ FL (FLAVOR-REDEFINITION-FOR-COMPILATION FL NIL)))) ! 2034: (OR (FLAVOR-DEPENDS-ON-ALL FL) ! 2035: (COMPOSE-FLAVOR-COMBINATION FL)) ! 2036: (COMPOSE-METHOD-COMBINATION FL NIL)))))) ! 2037: ! 2038: ;; Do the composition now. This should normally just generate data-structure ! 2039: ;; as the methods should already all have been compiled, unless something has changed. ! 2040: (DEFUN COMPILE-FLAVOR-METHODS-2 (FLAVOR-NAME &AUX FL) ! 2041: (CHECK-ARG FLAVOR-NAME (SETQ FL (GET FLAVOR-NAME 'FLAVOR)) "the name of a flavor") ! 2042: (PUTPROP (FLAVOR-PLIST FL) T 'COMPILE-FLAVOR-METHODS) ! 2043: (COND ((FLAVOR-COMPONENTS-DEFINED-P FLAVOR-NAME) ! 2044: (OR (FLAVOR-DEPENDS-ON-ALL FL) (COMPOSE-FLAVOR-COMBINATION FL)) ! 2045: (OR (FLAVOR-METHOD-HASH-TABLE FL) ! 2046: (COMPOSE-METHOD-COMBINATION FL)))) ! 2047: FLAVOR-NAME) ! 2048: ! 2049: ;Returns T if all components of this flavor are defined ! 2050: (DEFUN FLAVOR-COMPONENTS-DEFINED-P (FLAVOR-NAME &OPTIONAL COMPLAINT &AUX FL) ! 2051: (COND ((SETQ FL (GET-FLAVOR FLAVOR-NAME)) ! 2052: (OR (NOT (NULL (FLAVOR-DEPENDS-ON-ALL FL))) ;Already composed, be fast ! 2053: (AND (DO ((L (FLAVOR-DEPENDS-ON FL) (CDR L))) ((NULL L) T) ! 2054: (OR (FLAVOR-COMPONENTS-DEFINED-P (CAR L)) (RETURN NIL))) ! 2055: (DO ((L (FLAVOR-INCLUDES FL) (CDR L))) ((NULL L) T) ! 2056: (OR (FLAVOR-COMPONENTS-DEFINED-P (CAR L)) (RETURN NIL)))))) ! 2057: (COMPLAINT (FORMAT ERRPORT "~&~A - ~S undefined flavor" COMPLAINT FLAVOR-NAME) ! 2058: NIL) ! 2059: (T NIL))) ! 2060: ! 2061: (EVAL-WHEN (EVAL LOAD) (LOAD 'VANILLA)) ! 2062: ! 2063: ;; Local Modes: ! 2064: ;; Mode: Lisp ! 2065: ;; Case Search: 1 ! 2066: ;; End:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.