|
|
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.