Google
 

Trailing-Edge - PDP-10 Archives - decus_20tap1_198111 - decus/20-0004/19lisp.doc
There are no other files named 19lisp.doc in the archive.











                               SECTION 19

                                       1
                               ADVISING




The operation of advising gives  the user a way of modifying  a function
without necessarily knowing how the function works or even what it does.
Advising  consists  of  modifying  the  interface  between  functions as
opposed  to modifying  the function  definition itself,  as  in editing.
break, trace, and breakdown, are examples of the use of  this technique:
they each modify user functions by placing relevant computations between
the function and the rest of the programming environment.

The principal advantage of advising, aside from its convenience, is that
it allows the user to treat functions, his or someone else's,  as "black
boxes," and to modify them without concern for their contents or details
of operations.  For example, the user could modify sysout to set sysdate
to the time and date of creation by advise[SYSOUT;(SETQ SYSDATE (DATE))]

As with break, advising  works equally well on compiled  and interpreted
functions.  Similarly,  it is  possible to  effect a  modification which
only  operates  when a  function  is called  from  some  other specified
function,  i.e.,  to   modify  the  interface  between   two  particular
functions, instead of the interface between one function and the rest of
the world.  This  latter feature is  especially useful for  changing the
internal workings of a system function.

For example,  suppose the  user wanted  time (Section  21) to  print the
results of his measurements to the file FOO instead of the teletype.  He
could                 accomplish                 this                 by
ADVISE(((PRIN1 PRINT SPACES) IN TIME) BEFORE (SETQQ U FOO))

Note that advising prin1, print, or spaces directly would  have affected
all  calls  to these  very  frequently used  function,  whereas advising
((PRIN1 PRINT SPACES) IN TIME) affects just those calls to prin1, print,
and spaces from time.

Advice  can also  be  specified to  operate  after a  function  has been
evaluated.   The value  of  the body  of  the original  function  can be
obtained from the variable !value, as with break1.  For example, suppose
the user wanted to perform some computation following each  sysin, e.g.,
check whether his files were up to date.  He could then:


------------------------------------------------------------------------
1
    Advising was developed and implemented by W. Teitelman.




                                  19.1



                                                        2
         ADVISE(SYSOUT AFTER (COND ((LISTP !VALUE) --)))


19.1 Implementation of Advising

The structure of a function after it has been modified several  times by
advise is given in the following diagram:
























                              Figure 19-1




















------------------------------------------------------------------------
2
    After the sysin, the  system will be as  it was when the  sysout was
    performed,  hence  the advice  must  be to  sysout,  not  sysin. See
    Section 14 for complete discussion of sysout/sysin.




                                  19.2



The corresponding INTERLISP definition is:

              (LAMBDA arguments (PROG (!VALUE)
                   (SETQ !VALUE (PROG NIL
                         advice1
                           .
                           .
ADVICE
                           .
BEFORE
                         advicen
                         (RETURN body)))
                    advice1
                     .
                     .       ADVICE
                     .       AFTER
                    advicem
                    (RETURN !VALUE)))

                                                    3 4
where body is equivalent to the original definition.  

Note that the structure of a function modified by advise allows  a piece
of  advice  to bypass  the  original definition  by  using  the function
RETURN.  For  example, if (COND ((ATOM X) (RETURN Y)))  were one  of the
pieces of advice BEFORE a function, and this function was entered with x
atomic, y would be returned as the value of the inner PROG, !value would
be set to y,  and control passed to the  advice, if any, to  be executed
AFTER the  function.  If this  same piece of  advice appeared  AFTER the
function,  y  would be  returned  as  the value  of  the  entire advised
function.

The  advice (COND ((ATOM X) (SETQ !VALUE Y))) AFTER  the  function would
have a  similar effect, but  the rest of  the advice AFTER  the function
would still be executed.













------------------------------------------------------------------------
3
    Actually, advise uses  its own versions  of PROG, SETQ,  and RETURN,
    (called  ADV-PROG,  ADV-SETQ,  and ADV-RETURN)  in  order  to enable
    advising these functions.

4
    If fn was  originally an EXPR, body  is the body of  the definition,
    otherwise a form using a  gensym which is defined with  the original
    definition.




                                  19.3



19.2 Advise Functions

Advise

Advise is a function of  four arguments: fn, when, where, and  what.  fn
is the function to be modified by advising, what is the modification, or
piece of advice.  when is either BEFORE, AFTER, or AROUND, and indicates
whether the advice  is to operate BEFORE,  AFTER, or AROUND the  body of
the function definition.  where  specifies exactly where in the  list of
advice the new  advice is to be  placed, e.g., FIRST, or  (BEFORE PRINT)
meaning before the advice  containing print, or (AFTER 3)  meaning after
the third  piece of advice,  or even (: TTY:).   If where  is specified,
advise first checks to see if it is one of LAST, BOTTOM, END,  FIRST, or
TOP, and operates accordingly.  Otherwise, it constructs  an appropriate
edit  command  and  calls  the  editor  to  insert  the  advice  at  the
corresponding location.

Both when and where are  optional arguments, in the sense that  they can
be omitted in the call to advise.  In other words, advise can be thought
of as  a function  of two arguments  [fn;what], or  a function  of three
arguments:   [fn;when;what],   or   a   function   of   four  arguments:
[fn;when;where;what].  Note that the advice is always the last argument.
If when=NIL, BEFORE is used.  If where=NIL, LAST is used.


advise[fn;when;where;what]
                        fn is the  function to be  advised, when=BEFORE,
                        AFTER, or AROUND,  where specifies where  in the
                        advice list  the advice is  to be  inserted, and
                        what is the piece of advice.

                        If  fn  is  of  the  form  (fn1 IN fn2),  fn1 is
                        changed  to fn1-IN-fn2  throughout fn2,  as with
                        break, and then  fn1-IN-fn2 is used in  place of
                           5
                        fn.

                        If fn is broken, it is unbroken before advising.

                        If  fn is  not defined,  an error  is generated,
                        NOT A FUNCTION.

                        If fn is being advised for the first time, i.e.,
                        if getp[name,ADVISED]=NIL, a gensym is generated
                        and stored on the property list of fn  under the
                        property ADVISED, and the gensym is defined with
                        the original  definition of fn.   An appropriate




------------------------------------------------------------------------
5
    If fn1 and/or  fn2 are lists, they  are distributed as shown  in the
    example on page 19.1.







                                  19.4


                                                                       6
                        S-expression definition is then created for fn.
                        Finally,  fn   is  added   to  the   (front  of)
                                   7
                        advisedfns.

                        If fn  has been advised  before, it is  moved to
                        the front of advisedfns.

                        If when=BEFORE or AFTER, the advice  is inserted
                        in fn's  definition either  BEFORE or  AFTER the
                        original  body  of  the  function.   Within that
                        context,  its position  is determined  by where.
                        If where=LAST, BOTTOM,  END, or NIL,  the advice
                        is added following all other advice, if any.  If
                        where=FIRST or  TOP, the  advice is  inserted as
                        the first piece of advice.  Otherwise,  where is
                        treated  as  a  command  for  the  editor,  a la
                        breakin, e.g., (BEFORE 3), (AFTER PRINT) .

                        If when=AROUND, the body is substituted for * in
                        the advice, and the result becomes the new body,
                        e.g.,
                        advise[FOO;AROUND;(RESETFORM (OUTPUT T) *)].
                        Note that if several pieces of AROUND advice are
                        specified, earlier ones will be  embedded inside
                        later ones. The value of where is ignored.

                        Finally   list[when;where;what]  is   added  (by
                        addprop) to the value of property ADVICE  on the
                                         8
                        property list fn.  Note that this property value
                        is a  list of  the advice in  order of  calls to
                        advise, not  necessarily in order  of appearance
                        of the advice in the definition of fn.

                        The value of advise is fn.

                        If  fn is  non-atomic, every  function in  fn is
                        advised with  the same  values (but  copies) for
                        when, where, and what.  In this case,  the value
                        of advise is a list of individual functions.

Note: advised functions can be broken.  (However if a function is broken



------------------------------------------------------------------------
6
    Using  private versions  of PROG,  SETQ, and  RETURN, so  that these
    functions can also be advised.

7
    So that unadvise[T] always unadvises the last function  advised. See
    page 19.6.

8
    So that a record of all the changes is available for  subsequent use
    in readvising, see page 19.6.




                                  19.5



at the  time it is  advised, it is  first unbroken.)  Similarly, advised
functions can  be edited, including  their advice.  unadvise  will still
restore the function to its unadvised state, but any changes to the body
of the definition will survive.  Since the advice stored on the property
list  is the  same structure  as the  advice inserted  in  the function,
editing of advice can  be performed on either the  function's definition
or its property list.


unadvise[x]             is a no-spread  NLAMBDA a la unbreak.   It takes
                        an indefinite  number of functions  and restores
                        them   to   their   original   unadvised  state,
                        including  removing  the  properties   added  by
                               9
                        advise.  unadvise  saves on the  list advinfolst
                        enough information to allow restoring a function
                        to its advised state using readvise.  advinfolst
                        and readvise  thus correspond to  brkinfolst and
                        rebreak.

                        unadvise[]    unadvises    all    functions   on
                                   10
                        advisedfns.   It first sets advinfolst to NIL.

                        unadvise[T]  unadvises  the  first  function  of
                        advisedfns,  i.e.,  the  most  recently  advised
                        function.


readvise[x]             is  a  no-spread   NLAMBDA  a  la   rebreak  for
                        restoring  a  function  to  its   advised  state
                        without  having   to  specify  all   the  advise
                        information.  For  each function on  x, readvise
                        retrieves the advise information either from the
                        property  READVICE  for that  function,  or from
                        advinfolst,   and  performs   the  corresponding
                        advise operation(s).  In addition it stores this
                        information  on  the  property  READVICE  if not
                        already there.  If no information is found for a
                        particular       function,        value       is
                        (fn - NO ADVICE SAVED).

                        readvise[] readvises everything on advinfolst.

                        readvise[T] readvises just the first function on
                        advinfolst,  i.e.,  the  function  most recently
                        unadvised.


------------------------------------------------------------------------
9
    Except  if  a  function also  contains  the  property  READVICE (see
    readvise below),  unadvise moves the  current value of  the property
    ADVICE to READVICE.

10
    In  reverse order,  so that  the most  recently advised  function is
    unadvised last.




                                  19.6



A  difference  between  advise,  unadvise,  and  readvise  versus break,
unbreak, and  rebreak, is  that if  a function  is not  rebroken between
successive unbreak[]'s,  its break  information is  forgotten.  However,
once readvised, a function's advice is permanently saved on its property
list (under READVICE); subsequent calls to unadvise will not  remove it.
In fact, calls to unadvise update the property READVICE with the current
value of  the property  ADVICE, so that  the sequence  readvise, advise,
unadvise causes the augmented advice to become permanent.  Note that the
sequence readvise, advise, readvise removes the "intermediate advice" by
restoring the function to its earlier state.


advisedump[x;flg]       Used by  prettydef when given  a command  of the
                        form    (ADVISE --)   or    (ADVICE --).   flg=T
                        corresponds  to  (ADVISE --),  i.e.,  advisedump
                        writes both a  deflist and a  readvise.  flg=NIL
                        corresponds  to  (ADVICE --),  i.e.,   only  the
                        deflist is written.  In either  case, advisedump
                        copies  the advise  information to  the property
                        READVICE,  thereby  making  it   "permanent"  as
                        described above.









































                                  19.7