Consider the following code.
define print_#( n ); lvars n;
lvars l = n.conslist;
applist( l, spr );
sys_grbg_list( l )
enddefine;
It is intended to be used as print_#(#| e1, ..., en |#), printing
several space-separated items without having to call ``pr'' lots of
times and without producing garbage.
The ``count brackets'' #|...|# evaluate the expressions between them
and also leave a count of the resulting values on the stack. Inside
``print_#'', these are made into a list, each item printed (``spr''
prints the item and then a space), and then the list returned to
free store.
[One could, with creative use of subscrstack, attempt to print the
items directly out of the stack. This fails miserably if ``printing''
leaves values on the stack, eg, by just pushing the successive
characters. This idiom is widespread enough that gratuitous violation
seems impolite.]
We know, of course, that we're the sole owners of the list, and so
it's Perfectly Safe to return it to the free store.
But we might be wrong.
The Paranoid Pop Programmer will note that ``spr'' will end up calling
the class_apply of the printed items, which are user-redefinable. So
this code can call arbitrary, user-selected, procedures.
For example, ``consproc_to'', which makes the current call chain (up
to a specified procedure) into a Pop process (sort-of co-routine). If
this process gets stashed, and run again later, the list (in) ``l'' will
get garbaged again, which could be rather unfortunate.
The Truly PPP will note that a timer interrupt could end up calling
``consproc_to'' in a similarly awkward way.
The moral of the story is twofold: you don't always know that you're the
only owner of some pointer to a data structure, and ``consproc_to''
should be employed only in carefully controlled ways.
--
Regards, Kers | "Once I would have been glad to have made your acquaintance
Renaissance: | Now I feel the danger brought about by circumstance."
|