BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.bin / lisp / lisplib / flavors.l
CommitLineData
95f51977
C
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\f
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\f
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\f
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\f
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\f
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\f
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\f
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\f
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\f
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\f
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\f
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~^, ~}~]
1381Requiring 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.
1418The 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\f
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\f
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\f
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\f
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\f
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: