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