1 // lispthrow.h                                 Copyright Codemist 2020-2021
2 
3 // Some exception processing stuff for CSL
4 
5 /**************************************************************************
6  * Copyright (C) 2021, Codemist.                         A C Norman       *
7  *                                                                        *
8  * Redistribution and use in source and binary forms, with or without     *
9  * modification, are permitted provided that the following conditions are *
10  * met:                                                                   *
11  *                                                                        *
12  *     * Redistributions of source code must retain the relevant          *
13  *       copyright notice, this list of conditions and the following      *
14  *       disclaimer.                                                      *
15  *     * Redistributions in binary form must reproduce the above          *
16  *       copyright notice, this list of conditions and the following      *
17  *       disclaimer in the documentation and/or other materials provided  *
18  *       with the distribution.                                           *
19  *                                                                        *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         *
24  * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   *
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR  *
29  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF     *
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
31  * DAMAGE.                                                                *
32  *************************************************************************/
33 
34 // $Id: lispthrow.h 5757 2021-04-08 16:36:14Z arthurcnorman $
35 
36 #ifndef __lispthrow_h
37 #define __lispthrow_h 1
38 
39 // If NO_THROW is defined this uses a flag rather than genuine C++ exceptions!
40 
41 #define TL_stack 49
42 DECLARE_THREAD_LOCAL(LispObject *, stack);
43 
44 // There is a "Lisp Stack" which is separate from the C++ stack. It has
45 // a number of uses:
46 // (1) Prior to the conservative GC all references to heap data must be
47 //     somewhere "safe" whenever a GC could happen. This is achieved by
48 //     going { Save save(x); <dangerous operations>; save.restore(x); }
49 //     in many places.
50 //     This keeps the identification of heap pointers "precise".
51 // (2) The bytecode interpreter is a variety of stack-based computer with
52 //     two accumulators (A and B). It works by pushing and popping items
53 //     on the stack and accessing some relative to the stack top. In the
54 //     case of deep recursion this naturally lead to a substantial amount
55 //     of stuff ending up on the Lisp stack.
56 // (3) Some parts of the interpreter and the implementation of some special
57 //     forms used the Lisp stack either to marshall data where it is not
58 //     known in advance how much will be present or to keep precise pointers
59 //     to a significant number of values.
60 // When a conservative collector is in use case (1) above becomes an out of
61 // date constraint (that adds a level of inefficiency) and come cases of (3)
62 // could be reworked to have tidier code when there is no need for precise
63 // pointers. By and large (2) will remain. The code should now use RealSave
64 // in those cases where push and pop must use the Lisp stack and Save
65 // when the transfer is only needed for precision. So in the conservative
66 // case Save can represent a no-op while RealSave does something.
67 // If at some stage the precice GC is totally removed then all calls to
68 // just Save can be discarded.
69 //
70 // RealSave ALWAYS transfers values to the stack, so within the code indicated
71 // by the "..." you can access them via stack[-n], but use of save.val() is
72 // preferable.
73 // For the version of CSL before there is a conservative garbage collector
74 // Save tranfers values to the stack, but it would be bad to try to access
75 // them there. When there is a conservative garbage collector both the
76 // constructors for Save and its restore() method will become no-ops because
77 // values can be kept safely in simple C++ variables. As a debugging help
78 // over the transition the conservative scheme does save values but then
79 // arranges that restore() verifies that the stacked value and the one in the
80 // simple variable have not got out of step.
81 
82 class PushCount
83 {
84 public:
85     int n;
PushCount(int count)86     PushCount(int count)
87     {   n = count;
88     }
89 };
90 
91 class RealSave
92 {
93 private:
94     LispObject *ssave;
95 public:
RealSave(PushCount count)96     RealSave(PushCount count)
97     {   ssave = stack;
98 // The coding here may look slightly unusual, but is written on the basis
99 // that on Windows having stack as a thread_local variable has a side
100 // effect of getting it treated as volatile, so every visible access to it
101 // turns into an explicit memory reference and optimisation of the code is
102 // inhibited to an extent that makes a significant difference to overall
103 // system performance!
104         stack = ssave + count.n;
105         for (int i=1; i<=count.n; i++)
106             ssave[i] = nil;
107     }
RealSave(LispObject a1)108     RealSave(LispObject a1)
109     {   ssave = stack;
110         stack = ssave + 1;
111         ssave[1] = a1;
112 #ifdef DEBUG
113 // In general after a function call that might return via an "exception"
114 // I should write "errexit()" so that if I build CSL in the mode where
115 // exceptions are simulated the simulation works. To help trap cases where
116 // I have failed to do this I have the concept of exception values" which
117 // are (mostly) delivered when a function exits "exceptionally". Such values
118 // ought never to end up being used. If I do see one I will abort!
119         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
120 #endif // DEBUG
121     }
RealSave(LispObject a1,PushCount count)122     RealSave(LispObject a1, PushCount count)
123     {   ssave = stack;
124         stack = ssave + count.n + 1;
125         ssave[1] = a1;
126 #ifdef DEBUG
127         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
128 #endif // DEBUG
129         for (int i=2; i<=count.n+1; i++)
130             ssave[i] = nil;
131     }
RealSave(LispObject a1,LispObject a2)132     RealSave(LispObject a1, LispObject a2)
133     {   ssave = stack;
134         stack = ssave + 2;
135         ssave[1] = a1;
136         ssave[2] = a2;
137 #ifdef DEBUG
138         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
139         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
140 #endif // DEBUG
141     }
RealSave(LispObject a1,LispObject a2,LispObject a3)142     RealSave(LispObject a1, LispObject a2, LispObject a3)
143     {   ssave = stack;
144         stack = ssave + 3;
145         ssave[1] = a1;
146         ssave[2] = a2;
147         ssave[3] = a3;
148 #ifdef DEBUG
149         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
150         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
151         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
152 #endif // DEBUG
153     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4)154     RealSave(LispObject a1, LispObject a2, LispObject a3,
155              LispObject a4)
156     {   ssave = stack;
157         stack = ssave + 4;
158         ssave[1] = a1;
159         ssave[2] = a2;
160         ssave[3] = a3;
161         ssave[4] = a4;
162 #ifdef DEBUG
163         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
164         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
165         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
166         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
167 #endif // DEBUG
168     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5)169     RealSave(LispObject a1, LispObject a2, LispObject a3,
170              LispObject a4, LispObject a5)
171     {   ssave = stack;
172         stack = ssave + 5;
173         ssave[1] = a1;
174         ssave[2] = a2;
175         ssave[3] = a3;
176         ssave[4] = a4;
177         ssave[5] = a5;
178 #ifdef DEBUG
179         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
180         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
181         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
182         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
183         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
184 #endif // DEBUG
185     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,PushCount count)186     RealSave(LispObject a1, LispObject a2, LispObject a3,
187              LispObject a4, LispObject a5, PushCount count)
188     {   ssave = stack;
189         stack = ssave + count.n + 5;
190         ssave[1] = a1;
191         ssave[2] = a2;
192         ssave[3] = a3;
193         ssave[4] = a4;
194         ssave[5] = a5;
195         for (int i=0; i<count.n; i++)
196             ssave[i+6] = nil;
197 #ifdef DEBUG
198         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
199         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
200         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
201         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
202         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
203 #endif // DEBUG
204     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,LispObject a6)205     RealSave(LispObject a1, LispObject a2, LispObject a3,
206              LispObject a4, LispObject a5, LispObject a6)
207     {   ssave = stack;
208         stack = ssave + 6;
209         ssave[1] = a1;
210         ssave[2] = a2;
211         ssave[3] = a3;
212         ssave[4] = a4;
213         ssave[5] = a5;
214         ssave[6] = a6;
215 #ifdef DEBUG
216         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
217         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
218         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
219         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
220         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
221         if (is_exception(a6)) UNLIKELY my_abort("exception value not trapped");
222 #endif // DEBUG
223     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,LispObject a6,LispObject a7)224     RealSave(LispObject a1, LispObject a2, LispObject a3,
225              LispObject a4, LispObject a5, LispObject a6,
226              LispObject a7)
227     {   ssave = stack;
228         stack = ssave + 7;
229         ssave[1] = a1;
230         ssave[2] = a2;
231         ssave[3] = a3;
232         ssave[4] = a4;
233         ssave[5] = a5;
234         ssave[6] = a6;
235         ssave[7] = a7;
236 #ifdef DEBUG
237         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
238         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
239         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
240         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
241         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
242         if (is_exception(a6)) UNLIKELY my_abort("exception value not trapped");
243         if (is_exception(a7)) UNLIKELY my_abort("exception value not trapped");
244 #endif // DEBUG
245     }
RealSave(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,LispObject a6,LispObject a7,LispObject a8)246     RealSave(LispObject a1, LispObject a2, LispObject a3,
247              LispObject a4, LispObject a5, LispObject a6,
248              LispObject a7, LispObject a8)
249     {   ssave = stack;
250         stack = ssave + 8;
251         ssave[1] = a1;
252         ssave[2] = a2;
253         ssave[3] = a3;
254         ssave[4] = a4;
255         ssave[5] = a5;
256         ssave[6] = a6;
257         ssave[7] = a7;
258         ssave[8] = a8;
259 #ifdef DEBUG
260         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
261         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
262         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
263         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
264         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
265         if (is_exception(a6)) UNLIKELY my_abort("exception value not trapped");
266         if (is_exception(a7)) UNLIKELY my_abort("exception value not trapped");
267         if (is_exception(a8)) UNLIKELY my_abort("exception value not trapped");
268 #endif // DEBUG
269     }
val(int n)270     LispObject &val(int n)
271     {   return ssave[n];
272     }
restore(LispObject & a1)273     void restore(LispObject &a1)
274     {   a1 = ssave[1];
275     }
restore(LispObject & a1,LispObject & a2)276     void restore(LispObject &a1, LispObject &a2)
277     {   a1 = ssave[1];
278         a2 = ssave[2];
279     }
restore(LispObject & a1,LispObject & a2,LispObject & a3)280     void restore(LispObject &a1, LispObject &a2, LispObject &a3)
281     {   a1 = ssave[1];
282         a2 = ssave[2];
283         a3 = ssave[3];
284     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4)285     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
286                  LispObject &a4)
287     {   a1 = ssave[1];
288         a2 = ssave[2];
289         a3 = ssave[3];
290         a4 = ssave[4];
291     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5)292     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
293                  LispObject &a4, LispObject &a5)
294     {   a1 = ssave[1];
295         a2 = ssave[2];
296         a3 = ssave[3];
297         a4 = ssave[4];
298         a5 = ssave[5];
299     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5,LispObject & a6)300     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
301                  LispObject &a4, LispObject &a5, LispObject &a6)
302     {   a1 = ssave[1];
303         a2 = ssave[2];
304         a3 = ssave[3];
305         a4 = ssave[4];
306         a5 = ssave[5];
307         a6 = ssave[6];
308     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5,LispObject & a6,LispObject & a7)309     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
310                  LispObject &a4, LispObject &a5, LispObject &a6,
311                  LispObject &a7)
312     {   a1 = ssave[1];
313         a2 = ssave[2];
314         a3 = ssave[3];
315         a4 = ssave[4];
316         a5 = ssave[5];
317         a6 = ssave[6];
318         a7 = ssave[7];
319     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5,LispObject & a6,LispObject & a7,LispObject & a8)320     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
321                  LispObject &a4, LispObject &a5, LispObject &a6,
322                  LispObject &a7, LispObject &a8)
323     {   a1 = ssave[1];
324         a2 = ssave[2];
325         a3 = ssave[3];
326         a4 = ssave[4];
327         a5 = ssave[5];
328         a6 = ssave[6];
329         a7 = ssave[7];
330         a8 = ssave[8];
331     }
~RealSave()332     ~RealSave()
333     {   stack = ssave;
334     }
335 };
336 
337 // With a conservative GC I will want real_push and real_pop to exist and
338 // move things to and from a dedicated Lisp stack (eg as part of the way
339 // I handle some special forms or functions with huge numbers of arguments)
340 // but case where it used to be push/pop can replace those with no-operation.
341 
342 #ifdef CONSERVATIVE
343 
344 #if 0 // TEMP
345 
346 // The version here actually saves things to the stack although that ought
347 // not to be necessary. It was provided so I could test some parts of the
348 // general system before the GC was actually conservative...
349 
350 class Save
351 {
352 private:
353     LispObject *ssave;
354 public:
355 //  Save(PushCount count)
356 //  {   ssave = stack;
357 //      stack = ssave + count.n;
358 //      for (int i=1; i<=count.n; i++)
359 //          ssave[i] = nil;
360 //  }
361     Save(LispObject a1)
362     {   ssave = stack;
363         stack = ssave + 1;
364         ssave[1] = a1;
365 #ifdef DEBUG
366         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
367 #endif // DEBUG
368     }
369     Save(LispObject a1, LispObject a2)
370     {   ssave = stack;
371         stack = ssave + 2;
372         ssave[1] = a1;
373         ssave[2] = a2;
374 #ifdef DEBUG
375         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
376         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
377 #endif // DEBUG
378     }
379     Save(LispObject a1, LispObject a2, LispObject a3)
380     {   ssave = stack;
381         stack = ssave + 3;
382         ssave[1] = a1;
383         ssave[2] = a2;
384         ssave[3] = a3;
385 #ifdef DEBUG
386         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
387         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
388         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
389 #endif // DEBUG
390     }
391     Save(LispObject a1, LispObject a2, LispObject a3,
392              LispObject a4)
393     {   ssave = stack;
394         stack = ssave + 4;
395         ssave[1] = a1;
396         ssave[2] = a2;
397         ssave[3] = a3;
398         ssave[4] = a4;
399 #ifdef DEBUG
400         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
401         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
402         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
403         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
404 #endif // DEBUG
405     }
406     Save(LispObject a1, LispObject a2, LispObject a3,
407              LispObject a4, LispObject a5)
408     {   ssave = stack;
409         stack = ssave + 5;
410         ssave[1] = a1;
411         ssave[2] = a2;
412         ssave[3] = a3;
413         ssave[4] = a4;
414         ssave[5] = a5;
415 #ifdef DEBUG
416         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
417         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
418         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
419         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
420         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
421 #endif // DEBUG
422     }
423     Save(LispObject a1, LispObject a2, LispObject a3,
424              LispObject a4, LispObject a5, LispObject a6)
425     {   ssave = stack;
426         stack = ssave + 6;
427         ssave[1] = a1;
428         ssave[2] = a2;
429         ssave[3] = a3;
430         ssave[4] = a4;
431         ssave[5] = a5;
432         ssave[6] = a6;
433 #ifdef DEBUG
434         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
435         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
436         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
437         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
438         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
439         if (is_exception(a6)) UNLIKELY my_abort("exception value not trapped");
440 #endif // DEBUG
441     }
442     void restore(LispObject &a1)
443     {   a1 = ssave[1];
444     }
445     void restore(LispObject &a1, LispObject &a2)
446     {   a1 = ssave[1];
447         a2 = ssave[2];
448     }
449     void restore(LispObject &a1, LispObject &a2, LispObject &a3)
450     {   a1 = ssave[1];
451         a2 = ssave[2];
452         a3 = ssave[3];
453     }
454     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
455                  LispObject &a4)
456     {   a1 = ssave[1];
457         a2 = ssave[2];
458         a3 = ssave[3];
459         a4 = ssave[4];
460     }
461     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
462                  LispObject &a4, LispObject &a5)
463     {   a1 = ssave[1];
464         a2 = ssave[2];
465         a3 = ssave[3];
466         a4 = ssave[4];
467         a5 = ssave[5];
468     }
469     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
470                  LispObject &a4, LispObject &a5, LispObject &a6)
471     {   a1 = ssave[1];
472         a2 = ssave[2];
473         a3 = ssave[3];
474         a4 = ssave[4];
475         a5 = ssave[5];
476         a6 = ssave[6];
477     }
478     ~Save()
479     {   stack = ssave;
480     }
481 };
482 
483 #else // TEMP
484 
485 // Here is the pure noop version which should eventually be used while I am
486 // in the process of textually removing mention of Save all together!
487 // Trying the code with it enabled will let me observe just how much
488 // overhead the save-to-stack discipline imposes.
489 
490 class Save
491 {
492 public:
Save(LispObject a1)493     Save(LispObject a1)
494     {
495     }
Save(LispObject a1,LispObject a2)496     Save(LispObject a1, LispObject a2)
497     {
498     }
Save(LispObject a1,LispObject a2,LispObject a3)499     Save(LispObject a1, LispObject a2, LispObject a3)
500     {
501     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4)502     Save(LispObject a1, LispObject a2, LispObject a3,
503              LispObject a4)
504     {
505     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5)506     Save(LispObject a1, LispObject a2, LispObject a3,
507              LispObject a4, LispObject a5)
508     {
509     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,LispObject a6)510     Save(LispObject a1, LispObject a2, LispObject a3,
511              LispObject a4, LispObject a5, LispObject a6)
512     {
513     }
restore(LispObject & a1)514     void restore(LispObject &a1)
515     {
516     }
restore(LispObject & a1,LispObject & a2)517     void restore(LispObject &a1, LispObject &a2)
518     {
519     }
restore(LispObject & a1,LispObject & a2,LispObject & a3)520     void restore(LispObject &a1, LispObject &a2, LispObject &a3)
521     {
522     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4)523     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
524                  LispObject &a4)
525     {
526     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5)527     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
528                  LispObject &a4, LispObject &a5)
529     {
530     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5,LispObject & a6)531     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
532                  LispObject &a4, LispObject &a5, LispObject &a6)
533     {
534     }
~Save()535     ~Save()
536     {
537     }
538 };
539 #endif // TEMP
540 
541 #else // CONSERVATIVE
542 
543 class Save
544 {
545 private:
546     LispObject *ssave;
547 public:
548 //  Save(PushCount count)
549 //  {   ssave = stack;
550 //      stack = ssave + count.n;
551 //      for (int i=1; i<=count.n; i++)
552 //          ssave[i] = nil;
553 //  }
Save(LispObject a1)554     Save(LispObject a1)
555     {   ssave = stack;
556         stack = ssave + 1;
557         ssave[1] = a1;
558 #ifdef DEBUG
559         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
560 #endif // DEBUG
561     }
Save(LispObject a1,LispObject a2)562     Save(LispObject a1, LispObject a2)
563     {   ssave = stack;
564         stack = ssave + 2;
565         ssave[1] = a1;
566         ssave[2] = a2;
567 #ifdef DEBUG
568         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
569         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
570 #endif // DEBUG
571     }
Save(LispObject a1,LispObject a2,LispObject a3)572     Save(LispObject a1, LispObject a2, LispObject a3)
573     {   ssave = stack;
574         stack = ssave + 3;
575         ssave[1] = a1;
576         ssave[2] = a2;
577         ssave[3] = a3;
578 #ifdef DEBUG
579         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
580         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
581         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
582 #endif // DEBUG
583     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4)584     Save(LispObject a1, LispObject a2, LispObject a3,
585              LispObject a4)
586     {   ssave = stack;
587         stack = ssave + 4;
588         ssave[1] = a1;
589         ssave[2] = a2;
590         ssave[3] = a3;
591         ssave[4] = a4;
592 #ifdef DEBUG
593         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
594         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
595         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
596         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
597 #endif // DEBUG
598     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5)599     Save(LispObject a1, LispObject a2, LispObject a3,
600              LispObject a4, LispObject a5)
601     {   ssave = stack;
602         stack = ssave + 5;
603         ssave[1] = a1;
604         ssave[2] = a2;
605         ssave[3] = a3;
606         ssave[4] = a4;
607         ssave[5] = a5;
608 #ifdef DEBUG
609         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
610         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
611         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
612         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
613         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
614 #endif // DEBUG
615     }
Save(LispObject a1,LispObject a2,LispObject a3,LispObject a4,LispObject a5,LispObject a6)616     Save(LispObject a1, LispObject a2, LispObject a3,
617              LispObject a4, LispObject a5, LispObject a6)
618     {   ssave = stack;
619         stack = ssave + 6;
620         ssave[1] = a1;
621         ssave[2] = a2;
622         ssave[3] = a3;
623         ssave[4] = a4;
624         ssave[5] = a5;
625         ssave[6] = a6;
626 #ifdef DEBUG
627         if (is_exception(a1)) UNLIKELY my_abort("exception value not trapped");
628         if (is_exception(a2)) UNLIKELY my_abort("exception value not trapped");
629         if (is_exception(a3)) UNLIKELY my_abort("exception value not trapped");
630         if (is_exception(a4)) UNLIKELY my_abort("exception value not trapped");
631         if (is_exception(a5)) UNLIKELY my_abort("exception value not trapped");
632         if (is_exception(a6)) UNLIKELY my_abort("exception value not trapped");
633 #endif // DEBUG
634     }
635 
restore(LispObject & a1)636     void restore(LispObject &a1)
637     {   a1 = ssave[1];
638     }
restore(LispObject & a1,LispObject & a2)639     void restore(LispObject &a1, LispObject &a2)
640     {   a1 = ssave[1];
641         a2 = ssave[2];
642     }
restore(LispObject & a1,LispObject & a2,LispObject & a3)643     void restore(LispObject &a1, LispObject &a2, LispObject &a3)
644     {   a1 = ssave[1];
645         a2 = ssave[2];
646         a3 = ssave[3];
647     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4)648     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
649                  LispObject &a4)
650     {   a1 = ssave[1];
651         a2 = ssave[2];
652         a3 = ssave[3];
653         a4 = ssave[4];
654     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5)655     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
656                  LispObject &a4, LispObject &a5)
657     {   a1 = ssave[1];
658         a2 = ssave[2];
659         a3 = ssave[3];
660         a4 = ssave[4];
661         a5 = ssave[5];
662     }
restore(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4,LispObject & a5,LispObject & a6)663     void restore(LispObject &a1, LispObject &a2, LispObject &a3,
664                  LispObject &a4, LispObject &a5, LispObject &a6)
665     {   a1 = ssave[1];
666         a2 = ssave[2];
667         a3 = ssave[3];
668         a4 = ssave[4];
669         a5 = ssave[5];
670         a6 = ssave[6];
671     }
~Save()672     ~Save()
673     {   stack = ssave;
674     }
675 };
676 
677 #endif // CONSERVATIVE
678 
679 extern volatile bool tick_pending;
680 extern volatile int unwind_pending;
681 
682 extern LispObject respond_to_stack_event();
683 
stackcheck()684 inline void stackcheck()
685 {   if_check_stack();
686 // The next line uses a C style cast because at least in an experimental
687 // version of the code "stack" is an instance of a class not an item
688 // with basic type, and the generality provided by C style casts copes
689 // while use of (eg) reinterpret_cast does not.
690     if (((uintptr_t)stack | event_flag.load()) >=
691         reinterpret_cast<uintptr_t>(stackLimit)) respond_to_stack_event();
692 }
693 
stackcheck(LispObject & a1)694 inline void stackcheck(LispObject& a1)
695 {   if_check_stack();
696     if (((uintptr_t)stack | event_flag.load()) >=
697         reinterpret_cast<uintptr_t>(stackLimit))
698     {   Save saver(a1);
699         respond_to_stack_event();
700         saver.restore(a1);
701     }
702 }
703 
stackcheck(LispObject & a1,LispObject & a2)704 inline void stackcheck(LispObject& a1, LispObject& a2)
705 {   if_check_stack();
706     if (((uintptr_t)stack | event_flag.load()) >=
707         reinterpret_cast<uintptr_t>(stackLimit))
708     {   Save saver(a1, a2);
709         respond_to_stack_event();
710         saver.restore(a1, a2);
711     }
712 }
713 
stackcheck(LispObject & a1,LispObject & a2,LispObject & a3)714 inline void stackcheck(LispObject& a1, LispObject& a2, LispObject& a3)
715 {   if_check_stack();
716     if (((uintptr_t)stack | event_flag.load()) >=
717         reinterpret_cast<uintptr_t>(stackLimit))
718     {   Save saver(a1, a2, a3);
719         respond_to_stack_event();
720         saver.restore(a1, a2, a3);
721     }
722 }
723 
stackcheck(LispObject & a1,LispObject & a2,LispObject & a3,LispObject & a4)724 inline void stackcheck(LispObject& a1, LispObject& a2,
725                        LispObject& a3, LispObject& a4)
726 {   if_check_stack();
727     if (((uintptr_t)stack | event_flag.load()) >=
728         reinterpret_cast<uintptr_t>(stackLimit))
729     {   Save saver(a1, a2, a3, a4);
730         respond_to_stack_event();
731         saver.restore(a1, a2, a3, a4);
732     }
733 }
734 
respond_to_fringe_event(LispObject & r,const char * msg)735 inline void respond_to_fringe_event(LispObject &r, const char *msg)
736 {
737 // One possibility is that this is a genuine case of the current part of the
738 // heap having become full, and so I need to invoke garbage collection to
739 // try to tidy up.
740 #ifdef BOOTSTRAP
741 // The bootstrap version provides a special scheme that is present to
742 // help me debug storage management. It is set up by calling the Lisp-level
743 // function gc-forcer. That sets a variable force_cons and each time
744 // the system checks for space that is decremented. When it becomes zero
745 // the respond_to_fringe_event() function is called with its second
746 // argument nullptr. No fringes have been messed with. The system should just
747 // invoke the garbage collector and return. The intent here is to provide
748 // a way to force garbage collection at specific (if rather hard to compute!)
749 // moments.
750     if (msg == nullptr)
751     {
752 #ifdef CONSERVATIVE
753         reclaim("gc-forcer");
754 #else
755 // With a precise collector r is a variable that must be preserved.
756         r = reclaim(r, "gc-forcer", GC_USER_HARD, 0);
757 #endif
758         return;
759     }
760 #endif // BOOTSTRAP
761 //
762 // If an asynchronous event has arisen then event_flag has an interesting
763 // value. I want to read and reset it atomically, and these two lines
764 // using compare_exchange_weak() should achieve that.
765     uintptr_t f = event_flag.load();
766     while (!event_flag.compare_exchange_weak(f, 0)) {}
767 // Now one possibility is that this is a perfectly normal ordinary case
768 // for garbage collection because event_flag had been zero. In that case
769 // just garbage collect.
770     if (f == 0)
771     {
772 #ifdef CONSERVATIVE
773         reclaim(msg);
774 #else
775         r = reclaim(r, "gc-forcer", GC_USER_HARD, 0);
776 #endif
777         return;
778     }
779 }
780 
781 // Sometimes it could be that calls within the scope of a block might
782 // exit (eg via a throw) in a way that means that the exact state of the
783 // stack is uncertain. This resets it at block exit. It is liable to involve
784 // keeping the saveStack value around, and so stack_popper is to be
785 // preferred where it can be used.
786 
787 class stack_restorer
788 {   LispObject *saveStack;
789 public:
stack_restorer()790     stack_restorer()
791     {   saveStack = stack;
792     }
~stack_restorer()793     ~stack_restorer()
794     {   stack = saveStack;
795     }
796 };
797 
798 // I the interpreter need to save the variable current_function at times
799 
800 class save_current_function
801 {   LispObject *saveStack;
802 public:
save_current_function(LispObject newfn)803     save_current_function(LispObject newfn)
804     {   saveStack = stack;
805         stack = saveStack + 1;
806         saveStack[1] = current_function;
807         current_function = newfn;
808     }
~save_current_function()809     ~save_current_function()
810     {   current_function = saveStack[1];
811         stack = saveStack;
812     }
813 };
814 
815 // I am going to need to unbind fluids in the C++ code that I
816 // sometimes traslate Lisp into. Here is a helper class that will
817 // be useful for that.
818 
819 class bind_fluid_stack
820 {   LispObject *saveStack;
821     int env_loc;
822     int name_loc;
823     int val_loc;
824 public:
bind_fluid_stack(int e,int name,int val)825     bind_fluid_stack(int e, int name, int val)
826     {   saveStack = stack;
827         env_loc = e;
828         name_loc = name;
829         val_loc = val;
830 #ifdef TRACE_FLUID
831 // While I was debugging things being able to enable some printing here
832 // seemed a good idea!
833         debug_printf("bind_fluid_stack(%d, %d, %d) @ %p\n", e, name, val,
834                      stack);
835         debug_printf("name="); prin_to_debug(elt(saveStack[e], name));
836         debug_printf(" old-val=");
837         prin_to_debug(qvalue(elt(saveStack[e], name)));
838         debug_printf("\n");
839 #endif
840         saveStack[val] = qvalue(elt(saveStack[e], name));
841     }
~bind_fluid_stack()842     ~bind_fluid_stack()
843     {
844 #ifdef TRACE_FLUID
845         debug_printf("restore(%d, %d, %d) @ %p\n", env_loc, name_loc, val_loc,
846                      saveStack);
847         debug_printf("name=");
848         prin_to_debug(elt(saveStack[env_loc], name_loc));
849         debug_printf(" local-val=");
850         prin_to_debug(qvalue(elt(saveStack[env_loc], name_loc)));
851         debug_printf(" restored-val="); prin_to_debug(saveStack[val_loc]);
852         debug_printf("\n");
853 #endif
854         setvalue(elt(saveStack[env_loc], name_loc), saveStack[val_loc]);
855 // atomic????
856     }
857 };
858 
859 
860 
861 // I am going ti have some macros that provide a level of abstraction
862 // around "catch" and "throw". The reason for this is that in the special
863 // case when I use emscripten to compile my C++ code into Webassembly and
864 // Javascript there are very severe performance penalties for having any
865 // "catch" statements, including implicit ones used by RAII. So the default
866 // (and in 2020 about the only sane) use of emscripten disables support
867 // for exceptions and the C++ keyword "catch" is mostly treated as a no-op.
868 //
869 // Well when the code here was in C not C++ it did not have exceptions
870 // available, and it emulated them by setting an "exceptionFlag". In relevant
871 // cases my macros expand to something like the old version. The usage is
872 //
873 //      TRY
874 //        some statements
875 //        if (...) ... THROW(ExceptionName);
876 //        more statements
877 //      CATCH (ExceptionName)
878 //        recovery code
879 //        RETHROW;
880 //      ANOTHER_CATCH(AnotherExceptionName)
881 //        recovery code
882 //      END_CATCH;
883 //
884 // Note that there are no "{}" delimiters written. There are some severe
885 // constraints! First I have to use a limited number of "exception" types,
886 // and these do not carry data. System exceptions are NOT supported.
887 // The TRY..END_CATCH segment must be in a function that returns a value
888 // of type LispObject (!). The code between TRY and CATCH may not use
889 // control-flow statements such as return, continue or break to transfer
890 // beyond itself. If you use "return" beteen TRY and CATCH it will transfer
891 // control to where the CATCH checks for exceptions. The constrainst and
892 // the behaviour are because the body of the TRY is wrapped up as the
893 // body of a function (well a lambda-expression) so that within it THROW and
894 // the exception-testing macros can use "return" to escape from it. So
895 // if you look at the expanaion of TRY s1; CATCH(T) s2; END_CATCH; it is
896 // along the lines of
897 //    ([&]()->LispObject { s1; return nil })();
898 //    if (exceptionFlag & T) { exceptionFlag = LispNormal; s2; }
899 //    else if (exceptionFlag != LispNormal) return nil;
900 // and I think that the term "YUK" close to applies!
901 
902 
903 // I will have a number of exception types. I will NOT make them carry
904 // Lisp data with them even though that might seem reasonable. This is
905 // because during the processing of a throw some finalization can occur,
906 // and if some time that managed to cause garbage collection I would
907 // not be confident that the GC could find and the exception object to
908 // treat it as a list base. I will have a number of sub-classes of
909 // LispException just in case that ends up helping things be tidy.
910 
911 #ifdef NO_THROW
912 
913 enum LispExceptionTag
914 {
915 // LispNormal is for circumstances when no throw-like situation is in play.
916     LispNormal      = 0x00,
917 
918 // The next three are all varieties of error states.
919     LispError       = 0x03,
920     LispResource    = 0x01,
921 
922 // Now thee that are used to implement Lisp control structures within
923 // the interpreter.
924     LispGo          = 0x04,
925     LispReturnFrom  = 0x08,
926     LispThrow       = 0x10,
927 
928 // A final case exits from everything and then sometimes restarts.
929     LispRestart     = 0x20,
930 
931 // Any sort of the above.
932     LispException   = 0x3f
933 };
934 
935 // There were two ways I could have implemented software catch and thow.
936 // One sets a separate flag that can be checked anywhere that an exception
937 // might be pending, the other expands my domain of values with a special
938 // exception value and checks for that. I am going to do both here at least
939 // for the moment. The separate flag disrupts the transition less (I think)
940 // but returning a value SPID_ERROR as the dummy return value when an
941 // exception is raised os maybe a bit like having NaN for floating point:
942 // it can tends to persist and help me spot any cases where I failed to
943 // check for it.
944 
945 
946 #define SPID_LispException    (SPID_ERROR+(static_cast<int>(LispException)<<20))
947 #define SPID_Error            (SPID_ERROR+(static_cast<int>(LispError)<<20))
948 #define SPID_Resource         (SPID_ERROR+(static_cast<int>(LispResource)<<20))
949 #define SPID_Go               (SPID_ERROR+(static_cast<int>(LispGo)<<20))
950 #define SPID_ReturnFrom       (SPID_ERROR+(static_cast<int>(LispReturnFrom)<<20))
951 #define SPID_Throw            (SPID_ERROR+(static_cast<int>(LispThrow)<<20))
952 #define SPID_Restart          (SPID_ERROR+(static_cast<int>(LispRestart)<<20))
953 
954 
955 inline LispExceptionTag exceptionFlag = LispNormal;
956 
errorState()957 inline bool errorState()
958 {   return (exceptionFlag & LispError) != 0;
959 }
exceptionPending()960 inline bool exceptionPending()
961 {   return exceptionFlag != LispNormal;
962 }
963 
964 #define errexit()  if (exceptionPending()) UNLIKELY return SPID_Error
965 #define errexitint()  if (exceptionPending()) UNLIKELY return static_cast<int>(SPID_Error)
966 //#define errexitvoid()  if (exceptionPending()) UNLIKELY return
967 
968 #define TRY ([&]()->LispObject { SaveStack save_stack_Object ## __LINE__;
969 
970 // The next two variables are for debugging!
971 inline const char *exceptionFile = "none";
972 inline int exceptionLine = -1;
973 
974 #ifdef DEBUG
975 #define THROW(flavour) do {     \
976    exceptionFile = __FILE__;    \
977    exceptionLine = __LINE__;    \
978    exceptionFlag = flavour;     \
979    return SPID_Throw; } while(false)
980 #else
981 #define THROW(flavour)          \
982     do { exceptionFlag = flavour; return SPID_Throw; } while(false)
983 #endif
984 
985 #define CATCH(flavour) \
986    return nil;})(); if ((exceptionFlag & flavour) != 0) UNLIKELY \
987    {   [[maybe_unused]] LispExceptionTag saveException = exceptionFlag; exceptionFlag = LispNormal;
988 
989 #define ANOTHER_CATCH(flavour) \
990    } else if ((exceptionFlag & flavour) != 0) UNLIKELY \
991    {   [[maybe_unused]] LispExceptionTag saveException = exceptionFlag; exceptionFlag = LispNormal;
992 
993 #define RETHROW do { exceptionFlag = saveException; \
994                      return SPID_Error; } while(false)
995 
996 #define END_CATCH } else if (exceptionPending()) UNLIKELY return SPID_Error;
997 
998 #else // NO_THROW
999 
1000 struct LispException : public std::exception
whatLispException1001 {   virtual const char *what() const throw()
1002     {   return "Generic Lisp Exception";
1003     }
1004 };
1005 
1006 // Exceptions that count as "Errors" are or inherit from LispError, and
1007 // unwinding from one of them should lead to a backtrace.
1008 
1009 struct LispError : public LispException
whatLispError1010 {   virtual const char *what() const throw()
1011     {   return "Lisp Error";
1012     }
1013 };
1014 
1015 struct LispResource : public LispError
whatLispResource1016 {   virtual const char *what() const throw()
1017     {   return "Lisp Resouce Limiter";
1018     }
1019 };
1020 
1021 // Things that are not LispErrors are exceptions used to the system to
1022 // support Lisp features - GO, RETURN, THROW and RESTART.
1023 
1024 struct LispGo : public LispException
whatLispGo1025 {   virtual const char *what() const throw()
1026     {   return "Lisp Go";
1027     }
1028 };
1029 
1030 struct LispReturnFrom : public LispException
whatLispReturnFrom1031 {   virtual const char *what() const throw()
1032     {   return "Lisp ReturnFrom";
1033     }
1034 };
1035 
1036 struct LispThrow : public LispException
whatLispThrow1037 {   virtual const char *what() const throw()
1038     {   return "Lisp Throw";
1039     }
1040 };
1041 
1042 struct LispRestart : public LispException
whatLispRestart1043 {   virtual const char *what() const throw()
1044     {   return "Lisp Restart";
1045     }
1046 };
1047 
1048 // The following dynamic tests for exception conditions are not used in the
1049 // version of the code that uses "catch" and "throw".
1050 
exceptionPending()1051 inline bool exceptionPending()
1052 {   return false;
1053 }
1054 #define errexit()
1055 #define errexitint()
1056 #define errexitvoid()
1057 
1058 #define TRY try { ([&]()->LispObject { SaveStack save_stack_Object ## __LINE__;
1059 
1060 #define THROW(flavour) throw flavour()
1061 
1062 #define CATCH(flavour) return nil;})(); } catch (flavour &e) {
1063 
1064 #define ANOTHER_CATCH(flavour) } catch (flavour &e) {
1065 
1066 #define RETHROW throw
1067 #define RETHROWVOID throw
1068 
1069 #define END_CATCH }
1070 
1071 #endif // NO_THROW
1072 
1073 
1074 // If I build for debugging I will verify that the stack pointer is
1075 // properly unchanged across some scopes. This will help...
1076 
tidy_filename(const char * a)1077 inline const char *tidy_filename(const char *a)
1078 {   const char *b = std::strrchr(a, '/');
1079     return (b == nullptr ? a : b+1);
1080 }
1081 
1082 // If the (Lisp) stack were to get out of step with expectations the
1083 // consequences could be dire. To help me check against that I can use one
1084 // of these two macros. The second takes a LispObject that would then
1085 // appear in any diagnostics about stack confusion. If you are compiling
1086 // production code all that is generated is a null statement. But in debug
1087 // mode an object is created that recordsthe current stack pointer, and
1088 // when it goes out of scope at the end of the block it checks if things
1089 // have been put back as expected.
1090 
1091 #ifdef DEBUG
1092 
1093 class RAIIstack_sanity
1094 {   LispObject *saveStack;
1095     const char *fname;
1096     const char *file;
1097     int line;
1098     LispObject w;
1099 public:
RAIIstack_sanity(const char * fn,const char * fi,int li)1100     RAIIstack_sanity(const char *fn, const char *fi, int li)
1101     {   saveStack = stack;
1102         fname = fn;
1103         file = fi;
1104         line = li;
1105         w = nil;
1106     }
RAIIstack_sanity(const char * fn,const char * fi,int li,LispObject ww)1107     RAIIstack_sanity(const char *fn, const char *fi, int li,
1108                      LispObject ww)
1109     {   saveStack = stack;
1110         fname = fn;
1111         file = fi;
1112         line = li;
1113         w = ww;
1114     }
1115 // While I am unwinding the stack because of exception handling the stack
1116 // can remain un-restored. It is only once I have caught the exception
1117 // that it must end up correct. Hence get-out of exceptionFxlag is set.
~RAIIstack_sanity()1118     ~RAIIstack_sanity()
1119     {
1120 #ifdef NO_THROW
1121         if (saveStack != stack && exceptionFlag == LispNormal) UNLIKELY
1122 #else // NO_THROW
1123 #ifdef __cpp_lib_uncaught_exceptions
1124         if (saveStack != stack && std::uncaught_exceptions() == 0) UNLIKELY
1125 #else // __cpp_lib_uncaught_exceptions
1126         if (saveStack != stack && !std::uncaught_exception()) UNLIKELY
1127 #endif // __cpp_lib_uncaught_exceptions
1128 #endif // NO_THROW
1129         {   err_printf("[Stack Consistency fails] %p => %p in %s : %s:%d\n",
1130                        saveStack, stack, fname, file, line);
1131             err_printf("Data: ");
1132             prin_to_error(w);
1133             err_printf("\n");
1134             err_printf("exit_count = %d, exit_reason = %d\n",
1135                        exit_count, exit_reason);
1136             my_abort("stack consistency");
1137         }
1138     }
1139 };
1140 
1141 #define STACK_SANITY                                  \
1142     RAIIstack_sanity stack_sanity_object(__func__,    \
1143         tidy_filename(__FILE__), __LINE__);
1144 #define STACK_SANITY1(w)                              \
1145     RAIIstack_sanity stack_sanity_object(__func__,    \
1146         tidy_filename(__FILE__), __LINE__, w);
1147 
1148 #else
1149 
1150 #define STACK_SANITY            ;
1151 #define STACK_SANITY1(w)        ;
1152 
1153 #endif
1154 
1155 // In parts of the interpreter I want to save litvec and codevec and be
1156 // certain that I will restore them at function exit. This macro will help
1157 // me.
1158 // This mess may not be required when I have a conservative garbage collector
1159 // if I then make codevec and litvec local rather than global variables, and
1160 // the result might be both clearer code and less overhead.
1161 
1162 class RAIIsave_codevec
1163 {   LispObject *saveStack;
1164 public:
RAIIsave_codevec()1165     RAIIsave_codevec()
1166     {   saveStack = stack;
1167         stack = saveStack + 2;
1168         saveStack[1] = litvec;
1169         saveStack[2] = codevec;
1170     }
~RAIIsave_codevec()1171     ~RAIIsave_codevec()
1172     {   litvec = saveStack[1];
1173         codevec = saveStack[2];
1174         stack = saveStack;
1175     }
1176 };
1177 
1178 #define SAVE_CODEVEC RAIIsave_codevec save_codevec_object;
1179 
1180 
1181 // First I will comment on protection for push/pop against exceptions that
1182 // might arise, as in
1183 //    push(a, b);
1184 //    <exception or sigaction triggered here>
1185 //    pop(b, a);
1186 // I try take care to restore the stack pointer before returning from any
1187 // function.
1188 // Well for a time I had code that was slack about stack restoration
1189 // especially in the face of "throw" operations. But using the Save class
1190 // and RAII I hope I am repairing things.
1191 //
1192 // The places where I may depend on the stack pointer and so where it may
1193 // be prudent to take special care to keep a saved copy include...
1194 // (1) errorset
1195 // (2) Places where fluid variables are re-bound
1196 // (3) Any other place where global state is temporarily
1197 //     reset and needs to be restored at the end of an operation.
1198 //     Eg this may include "rdf" for the stream being read and
1199 //     perhaps explode and compress for the same sort of reason.
1200 // (4) Use of system resources that require finalization.
1201 // (5) Places where data structures are temporarily corrupted and then
1202 //     mended later.
1203 // (6) Some places where backtrace-style reports are called for.
1204 //
1205 // Let me try to comment a bit more on those.
1206 // (1) errorset needs to trap all errors. It should convert GO, RETURN-FROM
1207 //     and THROW into errors, but be transparent to RESTART and QUIT.
1208 // (2) fluids are bound in the interpreter code for LET, LET*, PROG,
1209 //     PROGV and in the bytecode engine. There are implicit fluid
1210 //     re-bindings of PACKAGE and maybe other things in some IO functions
1211 //     such as RDF. And also the interpreter code for LAMBDA and function
1212 //     application.
1213 // (3) Things like the current input and output streams need to be
1214 //     preserved across functions that use the mechanisms they involve,
1215 //     fo EXPLODE, COMPRESS, ... need review.
1216 // (4) Most obviously OPEN/use/CLOSE on files needs protection.
1217 // (5) The current implementation of some binding code reverses the
1218 //     lists of things to bind and then restores later on.
1219 // (6) Much of the interpreter and where the bytecode execution system is
1220 //     called needs to generate backtraces at times.
1221 //
1222 
1223 class SaveStack
1224 {   LispObject *saveStack;
1225 public:
SaveStack()1226     SaveStack()
1227     {   saveStack = stack;   // record stack value from entry here.
1228     }
~SaveStack()1229     ~SaveStack()
1230     {
1231 #if defined DEBUG && 0
1232         if (stack != saveStack) fprintf(stderr, "@@@ %d\n", (int)(stack - saveStack));
1233 #endif
1234         stack = saveStack;   // restore stack
1235     }
1236 };
1237 
1238 // The full mess I seem to want is ugly and bulky. I will try hiding it
1239 // away in a number of macros... so the user writes
1240 //    TRY
1241 //        <activity>
1242 //    CATCH(LispException)
1243 //        <whatever>
1244 //    END_CATCH
1245 
1246 // There are places where I need to display part of a backtrace when
1247 // unwinding the stack because of an error.
1248 //
1249 //    on_backtrace(do_something(arg1, arg2, arg3); // commas between args OK
1250 //                 do_something_more(),            // semicolon separators OK
1251 //                 // Now the error handler
1252 //                 printf("Error in %s\n", "something"));
1253 
1254 // In the code for both args of on_backtrace I must go
1255 //    if (exceptionPending()) break;
1256 // where relevant.
1257 
1258 #define on_backtrace(a, b)                  \
1259     TRY                                     \
1260         a;                                  \
1261     CATCH(LispError)                        \
1262         int _reason = exit_reason;          \
1263         b;                                  \
1264         exit_reason = _reason;              \
1265         RETHROW;                            \
1266     END_CATCH
1267 
1268 #define on_backtrace_void(a, b)             \
1269     TRY                                     \
1270         a;                                  \
1271     CATCH(LispError)                        \
1272         int _reason = exit_reason;          \
1273         b;                                  \
1274         exit_reason = _reason;              \
1275         RETHROWVOID;                        \
1276     END_CATCH
1277 
1278 
1279 // There are also places where I want to continue after error and
1280 // set a default value if some fragmement of computation fails, and
1281 // others where I wish to ignore errors entirely
1282 //
1283 //    if_error(a = construct_a_list(), a = nil);
1284 //    ignore_error(print_a_message());
1285 
1286 #define if_error(a, b)                       \
1287     TRY                                      \
1288         a;                                   \
1289     CATCH(LispError)                         \
1290         b;                                   \
1291     END_CATCH
1292 
1293 #define ignore_error(a)                       \
1294     TRY                                       \
1295         a;                                    \
1296     CATCH(LispError)                          \
1297     {}                                        \
1298     END_CATCH
1299 
1300 #endif // __lispthrow_h
1301 
1302 // end of lispthrow.h
1303