1 /*
2  * error.c - error handling
3  *
4  *   Copyright (c) 2000-2020  Shiro Kawai  <shiro@acm.org>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   3. Neither the name of the authors nor the names of its contributors
18  *      may be used to endorse or promote products derived from this
19  *      software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #define LIBGAUCHE_BODY
35 #include "gauche.h"
36 #include "gauche/class.h"
37 #include "gauche/exception.h"
38 #include "gauche/vm.h"
39 #include "gauche/priv/builtin-syms.h"
40 
41 #include <errno.h>
42 #include <string.h>
43 #include <ctype.h>
44 
45 static ScmObj condition_allocate(ScmClass *klass, ScmObj initargs);
46 static ScmObj message_allocate(ScmClass *klass, ScmObj initargs);
47 static ScmObj syserror_allocate(ScmClass *klass, ScmObj initargs);
48 static ScmObj sigerror_allocate(ScmClass *klass, ScmObj initargs);
49 static ScmObj readerror_allocate(ScmClass *klass, ScmObj initargs);
50 static ScmObj porterror_allocate(ScmClass *klass, ScmObj initargs);
51 static ScmObj compound_allocate(ScmClass *klass, ScmObj initargs);
52 
53 /* Setting up CPL is a bit tricky, since we have multiple
54    inheritance case. */
55 
56 #define CONDITION_CPL                           \
57     SCM_CLASS_STATIC_PTR(Scm_ConditionClass),   \
58     SCM_CLASS_STATIC_PTR(Scm_TopClass)
59 
60 #define MESSAGE_SERIOUS_CPL \
61     SCM_CLASS_STATIC_PTR(Scm_MessageConditionClass), \
62     SCM_CLASS_STATIC_PTR(Scm_SeriousConditionClass), \
63     CONDITION_CPL
64 
65 #define ERROR_CPL \
66     SCM_CLASS_STATIC_PTR(Scm_ErrorClass),        \
67     MESSAGE_SERIOUS_CPL
68 
69 /*-----------------------------------------------------------
70  * Base conditions
71  */
72 static ScmClass *condition_cpl[] = {
73     CONDITION_CPL,
74     NULL
75 };
76 
77 SCM_DEFINE_BASE_CLASS(Scm_ConditionClass, ScmInstance,
78                       NULL, NULL, NULL,
79                       condition_allocate, SCM_CLASS_DEFAULT_CPL);
80 SCM_DEFINE_BASE_CLASS(Scm_MessageConditionClass, ScmMessageCondition,
81                       Scm_MessageConditionPrint, NULL, NULL,
82                       message_allocate, condition_cpl);
83 SCM_DEFINE_BASE_CLASS(Scm_SeriousConditionClass, ScmSeriousCondition,
84                       NULL, NULL, NULL,
85                       condition_allocate, condition_cpl);
86 SCM_DEFINE_BASE_CLASS(Scm_MixinConditionClass, ScmCondition,
87                       NULL, NULL, NULL,
88                       condition_allocate, condition_cpl);
89 
condition_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)90 static ScmObj condition_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
91 {
92     return SCM_OBJ(SCM_NEW_INSTANCE(ScmCondition, klass));
93 }
94 
95 /* We expose this, for other condition subclasses may share this. */
Scm_MessageConditionPrint(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)96 void Scm_MessageConditionPrint(ScmObj obj, ScmPort *port,
97                                ScmWriteContext *ctx SCM_UNUSED)
98 {
99     ScmClass *k = Scm_ClassOf(obj);
100     Scm_Printf(port, "#<%A \"%30.1A\">",
101                Scm_ShortClassName(k),
102                SCM_ERROR_MESSAGE(obj));
103 }
104 
message_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)105 static ScmObj message_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
106 {
107     ScmError *e = SCM_NEW_INSTANCE(ScmError, klass);
108     e->message = SCM_FALSE;     /* would be set by initialize */
109     return SCM_OBJ(e);
110 }
111 
112 /* See comment on gauche/exception.h about hack in 'message' slot.
113    TRANSIENT: Remove this hack on 1.0 release. */
message_get(ScmMessageCondition * obj)114 static ScmObj message_get(ScmMessageCondition *obj)
115 {
116     ScmObj msglist = obj->message;
117     if (SCM_PAIRP(msglist)) return SCM_CAR(msglist);
118     else return msglist;
119 }
120 
message_set(ScmMessageCondition * obj,ScmObj val)121 static void message_set(ScmMessageCondition *obj, ScmObj val)
122 {
123     ScmObj msglist = obj->message;
124     if (SCM_PAIRP(msglist)) SCM_SET_CAR_UNCHECKED(msglist, val);
125     else SCM_MESSAGE_CONDITION(obj)->message = SCM_LIST2(val, val);
126 }
127 
message_prefix_get(ScmMessageCondition * obj)128 static ScmObj message_prefix_get(ScmMessageCondition *obj)
129 {
130     ScmObj msglist = obj->message;
131     if (SCM_PAIRP(msglist) && SCM_PAIRP(SCM_CDR(msglist))) {
132         return SCM_CADR(msglist);
133     } else {
134         return msglist;
135     }
136 }
137 
message_prefix_set(ScmMessageCondition * obj,ScmObj val)138 static void message_prefix_set(ScmMessageCondition *obj, ScmObj val)
139 {
140     ScmObj msglist = obj->message;
141     if (SCM_PAIRP(msglist) && SCM_PAIRP(SCM_CDR(msglist))) {
142         SCM_SET_CAR_UNCHECKED(SCM_CDR(msglist), val);
143     } else {
144         obj->message = SCM_LIST2(msglist, val);
145     }
146 }
147 
message_args_get(ScmMessageCondition * obj)148 static ScmObj message_args_get(ScmMessageCondition *obj)
149 {
150     ScmObj msglist = obj->message;
151     if (SCM_PAIRP(msglist) && SCM_PAIRP(SCM_CDR(msglist))) {
152         return SCM_CDDR(msglist);
153     } else {
154         return SCM_NIL;
155     }
156 }
157 
message_args_set(ScmMessageCondition * obj,ScmObj val)158 static void message_args_set(ScmMessageCondition *obj, ScmObj val)
159 {
160     ScmObj msglist = obj->message;
161     if (SCM_PAIRP(msglist) && SCM_PAIRP(SCM_CDR(msglist))) {
162         SCM_SET_CDR_UNCHECKED(SCM_CDR(msglist), val);
163     } else {
164         obj->message = Scm_Cons(msglist, Scm_Cons(msglist, val));
165     }
166 }
167 
168 static ScmClassStaticSlotSpec message_slots[] = {
169     SCM_CLASS_SLOT_SPEC("message", message_get, message_set),
170     SCM_CLASS_SLOT_SPEC("message-prefix",   message_prefix_get, message_prefix_set),
171     SCM_CLASS_SLOT_SPEC("message-args", message_args_get, message_args_set),
172     SCM_CLASS_SLOT_SPEC_END()
173 };
174 
175 /*------------------------------------------------------------
176  * Errors
177  */
178 
179 static ScmClass *error_cpl[] = {
180     ERROR_CPL,
181     NULL
182 };
183 
184 static ScmClass *porterror_cpl[] = {
185     SCM_CLASS_STATIC_PTR(Scm_PortErrorClass),
186     SCM_CLASS_STATIC_PTR(Scm_IOErrorClass),
187     ERROR_CPL,
188     NULL
189 };
190 
191 static ScmClass *decoding_error_cpl[] = {
192     SCM_CLASS_STATIC_PTR(Scm_IOReadErrorClass),
193     SCM_CLASS_STATIC_PTR(Scm_PortErrorClass),
194     SCM_CLASS_STATIC_PTR(Scm_IOErrorClass),
195     ERROR_CPL,
196     NULL
197 };
198 
199 static ScmClass *encoding_error_cpl[] = {
200     SCM_CLASS_STATIC_PTR(Scm_IOWriteErrorClass),
201     SCM_CLASS_STATIC_PTR(Scm_PortErrorClass),
202     SCM_CLASS_STATIC_PTR(Scm_IOErrorClass),
203     ERROR_CPL,
204     NULL
205 };
206 
207 SCM_DEFINE_BASE_CLASS(Scm_ErrorClass, ScmError,
208                       Scm_MessageConditionPrint, NULL, NULL,
209                       message_allocate, error_cpl+1);
210 SCM_DEFINE_BASE_CLASS(Scm_SystemErrorClass, ScmSystemError,
211                       Scm_MessageConditionPrint, NULL, NULL,
212                       syserror_allocate, error_cpl);
213 SCM_DEFINE_BASE_CLASS(Scm_UnhandledSignalErrorClass, ScmUnhandledSignalError,
214                       Scm_MessageConditionPrint, NULL, NULL,
215                       sigerror_allocate, error_cpl);
216 SCM_DEFINE_BASE_CLASS(Scm_ReadErrorClass, ScmReadError,
217                       Scm_MessageConditionPrint, NULL, NULL,
218                       readerror_allocate, error_cpl);
219 SCM_DEFINE_BASE_CLASS(Scm_IOErrorClass, ScmIOError,
220                       Scm_MessageConditionPrint, NULL, NULL,
221                       message_allocate, error_cpl);
222 SCM_DEFINE_BASE_CLASS(Scm_PortErrorClass, ScmPortError,
223                       Scm_MessageConditionPrint, NULL, NULL,
224                       porterror_allocate, porterror_cpl+1);
225 SCM_DEFINE_BASE_CLASS(Scm_IOReadErrorClass, ScmIOReadError,
226                       Scm_MessageConditionPrint, NULL, NULL,
227                       porterror_allocate, porterror_cpl);
228 SCM_DEFINE_BASE_CLASS(Scm_IOWriteErrorClass, ScmIOWriteError,
229                       Scm_MessageConditionPrint, NULL, NULL,
230                       porterror_allocate, porterror_cpl);
231 SCM_DEFINE_BASE_CLASS(Scm_IOClosedErrorClass, ScmIOClosedError,
232                       Scm_MessageConditionPrint, NULL, NULL,
233                       porterror_allocate, porterror_cpl);
234 SCM_DEFINE_BASE_CLASS(Scm_IOUnitErrorClass, ScmIOUnitError,
235                       Scm_MessageConditionPrint, NULL, NULL,
236                       porterror_allocate, porterror_cpl);
237 SCM_DEFINE_BASE_CLASS(Scm_IODecodingErrorClass, ScmIODecodingError,
238                       Scm_MessageConditionPrint, NULL, NULL,
239                       porterror_allocate, decoding_error_cpl);
240 SCM_DEFINE_BASE_CLASS(Scm_IOEncodingErrorClass, ScmIOEncodingError,
241                       Scm_MessageConditionPrint, NULL, NULL,
242                       porterror_allocate, encoding_error_cpl);
243 SCM_DEFINE_BASE_CLASS(Scm_IOInvalidPositionErrorClass, ScmIOInvalidPositionError,
244                       Scm_MessageConditionPrint, NULL, NULL,
245                       porterror_allocate, porterror_cpl);
246 
syserror_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)247 static ScmObj syserror_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
248 {
249     ScmSystemError *e = SCM_NEW_INSTANCE(ScmSystemError, klass);
250     e->common.message = SCM_FALSE; /* set by initialize */
251     e->error_number = 0;           /* set by initialize */
252     return SCM_OBJ(e);
253 }
254 
sigerror_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)255 static ScmObj sigerror_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
256 {
257     ScmUnhandledSignalError *e = SCM_NEW_INSTANCE(ScmUnhandledSignalError,
258                                                   klass);
259     e->common.message = SCM_FALSE; /* set by initialize */
260     e->signal = 0;                 /* set by initialize */
261     return SCM_OBJ(e);
262 }
263 
readerror_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)264 static ScmObj readerror_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
265 {
266     ScmReadError *e = SCM_NEW_INSTANCE(ScmReadError, klass);
267     e->common.message = SCM_FALSE; /* set by initialize */
268     e->port = NULL;                /* set by initialize */
269     e->line = -1;                  /* set by initialize */
270     return SCM_OBJ(e);
271 }
272 
porterror_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)273 static ScmObj porterror_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
274 {
275     ScmPortError *e = SCM_NEW_INSTANCE(ScmPortError, klass);
276     e->common.message = SCM_FALSE; /* set by initialize */
277     e->port = NULL;                /* set by initialize */
278     e->auxinfo = SCM_NIL;          /* set by initialize */
279     return SCM_OBJ(e);
280 }
281 
syserror_number_get(ScmSystemError * obj)282 static ScmObj syserror_number_get(ScmSystemError *obj)
283 {
284     return SCM_MAKE_INT(obj->error_number);
285 }
286 
syserror_number_set(ScmSystemError * obj,ScmObj val)287 static void syserror_number_set(ScmSystemError *obj, ScmObj val)
288 {
289     if (!SCM_INTP(val)) {
290         Scm_Error("small integer required, but got %S", val);
291     }
292     obj->error_number = SCM_INT_VALUE(val);
293 }
294 
sigerror_signal_get(ScmUnhandledSignalError * obj)295 static ScmObj sigerror_signal_get(ScmUnhandledSignalError *obj)
296 {
297     return SCM_MAKE_INT(obj->signal);
298 }
299 
sigerror_signal_set(ScmUnhandledSignalError * obj,ScmObj val)300 static void sigerror_signal_set(ScmUnhandledSignalError *obj, ScmObj val)
301 {
302     if (!SCM_INTP(val)) {
303         Scm_Error("small integer required, but got %S", val);
304     }
305     obj->signal = SCM_INT_VALUE(val);
306 }
307 
readerror_port_get(ScmReadError * obj)308 static ScmObj readerror_port_get(ScmReadError *obj)
309 {
310     if (obj->port) return SCM_OBJ(obj->port);
311     else return SCM_FALSE;
312 }
313 
readerror_port_set(ScmReadError * obj,ScmObj val)314 static void readerror_port_set(ScmReadError *obj, ScmObj val)
315 {
316     if (SCM_IPORTP(val)) {
317         obj->port = SCM_PORT(val);
318     }
319     else if (SCM_FALSEP(val)) {
320         obj->port = NULL;
321     }
322     else {
323         Scm_Error("input port or #f required, but got %S", val);
324     }
325 }
326 
readerror_line_get(ScmReadError * obj)327 static ScmObj readerror_line_get(ScmReadError *obj)
328 {
329     return SCM_MAKE_INT(obj->line);
330 }
331 
readerror_line_set(ScmReadError * obj,ScmObj val)332 static void readerror_line_set(ScmReadError *obj, ScmObj val)
333 {
334     if (!SCM_INTP(val)){
335         Scm_Error("small integer required, but got %S", val);
336     }
337     obj->line = SCM_INT_VALUE(val);
338 }
339 
readerror_dummy_get(ScmReadError * obj SCM_UNUSED)340 static ScmObj readerror_dummy_get(ScmReadError *obj SCM_UNUSED)
341 {
342     return SCM_FALSE;
343 }
344 
readerror_dummy_set(ScmReadError * obj SCM_UNUSED,ScmObj val SCM_UNUSED)345 static void readerror_dummy_set(ScmReadError *obj SCM_UNUSED,
346                                 ScmObj val SCM_UNUSED)
347 {
348     /* nothing */
349 }
350 
porterror_port_get(ScmPortError * obj)351 static ScmObj porterror_port_get(ScmPortError *obj)
352 {
353     return obj->port? SCM_OBJ(obj->port) : SCM_FALSE;
354 }
355 
porterror_port_set(ScmPortError * obj,ScmObj val)356 static void porterror_port_set(ScmPortError *obj, ScmObj val)
357 {
358     if (!SCM_PORTP(val) && !SCM_FALSEP(val)) {
359         Scm_Error("port or #f required, but got %S", val);
360     }
361     obj->port = SCM_FALSEP(val)? NULL : SCM_PORT(val);
362 }
363 
porterror_auxinfo_getter(ScmPortError * obj,ScmObj key)364 static ScmObj porterror_auxinfo_getter(ScmPortError *obj, ScmObj key)
365 {
366     ScmObj p = Scm_Assq(key, obj->auxinfo);
367     if (SCM_PAIRP(p)) return SCM_CDR(p);
368     return SCM_FALSE;
369 }
370 
porterror_auxinfo_setter(ScmPortError * obj,ScmObj key,ScmObj val)371 static void porterror_auxinfo_setter(ScmPortError *obj, ScmObj key, ScmObj val)
372 {
373     ScmObj p = Scm_Assq(key, obj->auxinfo);
374     if (SCM_PAIRP(p)) SCM_SET_CDR(p, val);
375     else obj->auxinfo = Scm_Acons(key, val, obj->auxinfo);
376 }
377 
378 static ScmObj sym_offending_char; /* 'offending-char */
379 
porterror_offending_char_get(ScmPortError * obj)380 static ScmObj porterror_offending_char_get(ScmPortError *obj)
381 {
382     return porterror_auxinfo_getter(obj, sym_offending_char);
383 }
384 
porterror_offending_char_set(ScmPortError * obj,ScmObj val)385 static void porterror_offending_char_set(ScmPortError *obj, ScmObj val)
386 {
387     porterror_auxinfo_setter(obj, sym_offending_char, val);
388 }
389 
390 static ScmObj sym_position;     /* 'position */
391 
porterror_position_get(ScmPortError * obj)392 static ScmObj porterror_position_get(ScmPortError *obj)
393 {
394     return porterror_auxinfo_getter(obj, sym_position);
395 }
396 
porterror_position_set(ScmPortError * obj,ScmObj val)397 static void porterror_position_set(ScmPortError *obj, ScmObj val)
398 {
399     porterror_auxinfo_setter(obj, sym_position, val);
400 }
401 
402 static ScmClassStaticSlotSpec syserror_slots[] = {
403     SCM_CLASS_SLOT_SPEC("errno", syserror_number_get, syserror_number_set),
404     SCM_CLASS_SLOT_SPEC_END()
405 };
406 
407 static ScmClassStaticSlotSpec sigerror_slots[] = {
408     SCM_CLASS_SLOT_SPEC("signal", sigerror_signal_get, sigerror_signal_set),
409     SCM_CLASS_SLOT_SPEC_END()
410 };
411 
412 static ScmClassStaticSlotSpec readerror_slots[] = {
413     SCM_CLASS_SLOT_SPEC("port", readerror_port_get, readerror_port_set),
414     SCM_CLASS_SLOT_SPEC("line", readerror_line_get, readerror_line_set),
415     SCM_CLASS_SLOT_SPEC("column", readerror_dummy_get, readerror_dummy_set),
416     SCM_CLASS_SLOT_SPEC("position", readerror_dummy_get, readerror_dummy_set),
417     SCM_CLASS_SLOT_SPEC("span", readerror_dummy_get, readerror_dummy_set),
418     SCM_CLASS_SLOT_SPEC_END()
419 };
420 
421 static ScmClassStaticSlotSpec porterror_slots[] = {
422     SCM_CLASS_SLOT_SPEC("port", porterror_port_get, porterror_port_set),
423     SCM_CLASS_SLOT_SPEC_END()
424 };
425 
426 static ScmClassStaticSlotSpec encodingerror_slots[] = {
427     SCM_CLASS_SLOT_SPEC("port", porterror_port_get, porterror_port_set),
428     SCM_CLASS_SLOT_SPEC("offending-char",
429                         porterror_offending_char_get,
430                         porterror_offending_char_set),
431     SCM_CLASS_SLOT_SPEC_END()
432 };
433 
434 static ScmClassStaticSlotSpec invalidpositionerror_slots[] = {
435     SCM_CLASS_SLOT_SPEC("port", porterror_port_get, porterror_port_set),
436     SCM_CLASS_SLOT_SPEC("position",
437                         porterror_position_get,
438                         porterror_position_set),
439     SCM_CLASS_SLOT_SPEC_END()
440 };
441 
442 
443 /*------------------------------------------------------------
444  * Compound conditions
445  */
446 
447 static ScmClass *compound_cpl[] = {
448     SCM_CLASS_STATIC_PTR(Scm_CompoundConditionClass),
449     SCM_CLASS_STATIC_PTR(Scm_SeriousConditionClass),
450     CONDITION_CPL,
451     NULL
452 };
453 
compound_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)454 static void compound_print(ScmObj obj, ScmPort *port,
455                            ScmWriteContext *ctx SCM_UNUSED)
456 {
457     ScmClass *k = Scm_ClassOf(obj);
458     Scm_Printf(port, "#<%A", Scm_ShortClassName(k));
459     ScmCompoundCondition *c = SCM_COMPOUND_CONDITION(obj);
460     ScmObj cp;
461     SCM_FOR_EACH(cp, c->conditions) {
462         Scm_Printf(port, " %A", SCM_CAR(cp));
463     }
464     Scm_Printf(port, ">");
465 }
466 
467 SCM_DEFINE_BASE_CLASS(Scm_CompoundConditionClass, ScmCompoundCondition,
468                       compound_print, NULL, NULL,
469                       compound_allocate, compound_cpl+2);
470 SCM_DEFINE_BASE_CLASS(Scm_SeriousCompoundConditionClass, ScmCompoundCondition,
471                       compound_print, NULL, NULL,
472                       compound_allocate, compound_cpl);
473 
compound_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)474 static ScmObj compound_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
475 {
476     ScmCompoundCondition *e = SCM_NEW_INSTANCE(ScmCompoundCondition, klass);
477     e->conditions = SCM_NIL;
478     return SCM_OBJ(e);
479 }
480 
Scm_MakeCompoundCondition(ScmObj conditions)481 ScmObj Scm_MakeCompoundCondition(ScmObj conditions)
482 {
483     ScmObj h = SCM_NIL, t = SCM_NIL;
484     int serious = FALSE;
485     int nconds = Scm_Length(conditions);
486 
487     /* some boundary cases */
488     if (nconds < 0) {
489         Scm_Error("Scm_MakeCompoundCondition: list required, but got %S",
490                   conditions);
491     }
492     if (nconds == 0) {
493         return compound_allocate(SCM_CLASS_COMPOUND_CONDITION, SCM_NIL);
494     }
495     if (nconds == 1) {
496         if (!SCM_CONDITIONP(SCM_CAR(conditions))) {
497             Scm_Error("make-compound-condition: given non-condition object: %S", SCM_CAR(conditions));
498         }
499         return SCM_CAR(conditions);
500     }
501 
502     /* collect conditions and creates compound one */
503     ScmObj cp;
504     SCM_FOR_EACH(cp, conditions) {
505         ScmObj c = SCM_CAR(cp);
506         if (!SCM_CONDITIONP(c)) {
507             Scm_Error("make-compound-condition: given non-condition object: %S", SCM_CAR(cp));
508         }
509         if (SCM_SERIOUS_CONDITION_P(c)) {
510             serious = TRUE;
511         }
512 
513         if (SCM_COMPOUND_CONDITION_P(c)) {
514             ScmCompoundCondition *cc = SCM_COMPOUND_CONDITION(c);
515             SCM_APPEND(h, t, cc->conditions);
516         } else {
517             SCM_APPEND1(h, t, c);
518         }
519     }
520     ScmObj cond = compound_allocate((serious?
521                                      SCM_CLASS_COMPOUND_CONDITION :
522                                      SCM_CLASS_SERIOUS_COMPOUND_CONDITION),
523                                     SCM_NIL);
524     SCM_COMPOUND_CONDITION(cond)->conditions = h;
525     return cond;
526 }
527 
528 /* Extract condition of type TYPE from a compound condition CONDITION.
529    The returned condition may be of subtype of TYPE.
530    CONDITION may be simple condition; in that case, CONDITION itself
531    is returned iff condition is of a subtype of TYPE.
532    This isn't the same as `extract-condition' of SRFI-35, which always
533    returns a condition of the specified type; it also has different
534    rule to refer to the slots.
535  */
Scm_ExtractSimpleCondition(ScmObj condition,ScmClass * type)536 ScmObj Scm_ExtractSimpleCondition(ScmObj condition, ScmClass *type)
537 {
538     ScmObj cs = (SCM_COMPOUND_CONDITION_P(condition)
539                  ? SCM_COMPOUND_CONDITION(condition)->conditions
540                  : SCM_LIST1(condition));
541     ScmObj cp;
542     SCM_FOR_EACH(cp, cs) {
543         if (SCM_ISA(SCM_CAR(cp), type)) return SCM_CAR(cp);
544     }
545     return SCM_FALSE;
546 }
547 
conditions_get(ScmCompoundCondition * obj)548 static ScmObj conditions_get(ScmCompoundCondition *obj)
549 {
550     return obj->conditions;
551 }
552 
conditions_set(ScmCompoundCondition * obj,ScmObj conds)553 static void   conditions_set(ScmCompoundCondition *obj, ScmObj conds)
554 {
555     ScmObj cp;
556     SCM_FOR_EACH(cp, conds) {
557         if (!SCM_CONDITIONP(SCM_CAR(cp))) goto err;
558     }
559     if (!SCM_NULLP(cp)) {
560       err:
561         Scm_Error("conditions slot of a compound condition must be a list of conditions, but got %S", conds);
562     }
563     obj->conditions = conds;
564 }
565 
566 static ScmClassStaticSlotSpec compound_slots[] = {
567     SCM_CLASS_SLOT_SPEC("%conditions", conditions_get, conditions_set),
568     SCM_CLASS_SLOT_SPEC_END()
569 };
570 
571 
572 /*
573  * C-level Constructors & generic API
574  */
575 
576 /* actual class structure of thread exceptions are in ext/threads */
Scm_MakeThreadException(ScmClass * klass,ScmVM * thread)577 ScmObj Scm_MakeThreadException(ScmClass *klass, ScmVM *thread)
578 {
579     ScmThreadException *e = SCM_NEW(ScmThreadException);
580     SCM_SET_CLASS(e, klass);
581     e->thread = thread;
582     e->data = SCM_UNDEFINED;
583     return SCM_OBJ(e);
584 }
585 
Scm_MakeError(ScmObj message)586 ScmObj Scm_MakeError(ScmObj message)
587 {
588     ScmError *e = SCM_ERROR(message_allocate(SCM_CLASS_ERROR, SCM_NIL));
589     e->message = SCM_LIST2(message, message);
590     return SCM_OBJ(e);
591 }
592 
Scm_MakeSystemError(ScmObj message,int en)593 ScmObj Scm_MakeSystemError(ScmObj message, int en)
594 {
595     ScmSystemError *e =
596         SCM_SYSTEM_ERROR(syserror_allocate(SCM_CLASS_SYSTEM_ERROR, SCM_NIL));
597     e->common.message = SCM_LIST2(message, message);
598     e->error_number = en;
599     return SCM_OBJ(e);
600 }
601 
Scm_MakeReadError(ScmObj message,ScmPort * port,int line)602 ScmObj Scm_MakeReadError(ScmObj message, ScmPort *port, int line)
603 {
604     ScmReadError *e =
605         SCM_READ_ERROR(readerror_allocate(SCM_CLASS_READ_ERROR, SCM_NIL));
606     e->common.message = SCM_LIST2(message, message);
607     e->port = port;
608     e->line = line;
609     return SCM_OBJ(e);
610 }
611 
Scm_ConditionHasType(ScmObj c,ScmObj k)612 int Scm_ConditionHasType(ScmObj c, ScmObj k)
613 {
614     if (!SCM_CONDITIONP(c)) return FALSE;
615     if (!SCM_CLASSP(k)) return FALSE;
616     if (!SCM_COMPOUND_CONDITION_P(c)) return SCM_ISA(c, SCM_CLASS(k));
617 
618     ScmObj cp;
619     SCM_FOR_EACH(cp, SCM_COMPOUND_CONDITION(c)->conditions) {
620         if (SCM_ISA(SCM_CAR(cp), SCM_CLASS(k))) return TRUE;
621     }
622     return FALSE;
623 }
624 
Scm_ConditionMessage(ScmObj c)625 ScmObj Scm_ConditionMessage(ScmObj c)
626 {
627     if (SCM_MESSAGE_CONDITION_P(c)) {
628         return message_get(SCM_MESSAGE_CONDITION(c));
629     } else if (SCM_COMPOUND_CONDITION_P(c)) {
630         ScmObj cp;
631         SCM_FOR_EACH(cp, SCM_COMPOUND_CONDITION(c)->conditions) {
632             if (SCM_MESSAGE_CONDITION_P(SCM_CAR(cp))) {
633                 return message_get(SCM_MESSAGE_CONDITION(SCM_CAR(cp)));
634             }
635         }
636     }
637     return SCM_FALSE;
638 }
639 
640 /* Returns a ScmString representiong the 'type name' of the condition,
641    suitable for the error message.  Because of personal preference
642    and backward compatibility, I upcase the class name of the condition
643    sans brackets.  If it is a composite condition, the component's typenames
644    are joind with commas, excluding mixin conditions.
645 */
Scm_ConditionTypeName(ScmObj c)646 ScmObj Scm_ConditionTypeName(ScmObj c)
647 {
648     ScmObj sname;
649     static SCM_DEFINE_STRING_CONST(cond_name_delim, ",", 1, 1);
650 
651     /* just a safety net */
652     if (!SCM_CONDITIONP(c)) return SCM_MAKE_STR("(not a condition)");
653 
654     if (!SCM_COMPOUND_CONDITION_P(c)) {
655         sname = Scm_ShortClassName(Scm_ClassOf(c));
656     } else {
657         ScmObj h = SCM_NIL, t = SCM_NIL, cp;
658         SCM_FOR_EACH(cp, SCM_COMPOUND_CONDITION(c)->conditions) {
659             ScmObj cc = SCM_CAR(cp);
660             if (SCM_MIXIN_CONDITION_P(cc)) continue;
661             SCM_APPEND1(h, t, Scm_ShortClassName(Scm_ClassOf(cc)));
662         }
663         if (SCM_NULLP(h)) {
664             /* not usual, but tolerate */
665             sname = Scm_ShortClassName(Scm_ClassOf(c));
666         } else {
667             sname = Scm_StringJoin(h, &cond_name_delim, SCM_STRING_JOIN_INFIX);
668         }
669     }
670 
671     ScmDString ds;
672     Scm_DStringInit(&ds);
673     ScmObj p = Scm_MakeInputStringPort(SCM_STRING(sname), TRUE);
674     int ch;
675     while ((ch = Scm_Getc(SCM_PORT(p))) != EOF) {
676         Scm_DStringPutc(&ds, Scm_CharUpcase(ch));
677     }
678     return Scm_DStringGet(&ds, 0);
679 }
680 
681 /*================================================================
682  * Error handling
683  *
684  *   The interaction with dynamic environment of VM is handled by
685  *   Scm_VMThrowException() in vm.c.   These routines provide
686  *   application interface.
687  *
688  *   Note on Windows system error: Windows may return error code
689  *   in two different ways; GetLastError for Windows API, and errno
690  *   for posix-compatibility calls.   When Scm_SysError is called
691  *   we don't know which error code to look at for sure.  As a
692  *   convention, we always clear both error code after Scm_SysError
693  *   and assumes whichever non-zero code indicates the actual error.
694  *   To map to integer error code, we reverse the sign of Windows
695  *   error code (Windows error code reserves full 32bit, but they don't
696  *   seem to use over 2^31 for the time being).
697  */
698 
699 /* Double fault check
700  * In order to aviod infinite loop when error throwing routine
701  * throws an error, we use vm flag SCM_ERROR_BEING_HANDLED to
702  * check that.  Ideally a common single API should handle it,
703  * but for the time being, we add the check at the beginning
704  * of Scm_*Error APIs.
705  * The SCM_ERROR_BEING_HANDLED flag is cleared in Scm_VMThrowException().
706  */
707 #define SCM_ERROR_DOUBLE_FAULT_CHECK(vm, msg)                           \
708     do {                                                                \
709         if (SCM_VM_RUNTIME_FLAG_IS_SET(vm, SCM_ERROR_BEING_HANDLED)) {  \
710             ScmDString ds;                                              \
711             Scm_DStringInit(&ds);                                       \
712             Scm_DStringPutz(&ds, "Error occurred in error handler (", -1); \
713             Scm_DStringPutz(&ds, msg, -1);                              \
714             Scm_DStringPutz(&ds, ")", -1);                              \
715             ScmObj e = Scm_MakeError(Scm_DStringGet(&ds, 0));           \
716             Scm_VMThrowException(vm, e, SCM_RAISE_NON_CONTINUABLE);     \
717         }                                                               \
718         SCM_VM_RUNTIME_FLAG_SET(vm, SCM_ERROR_BEING_HANDLED);           \
719     } while (0)
720 
721 /* Common part to format error message passed as varargs.
722  * ostr must be declared as ScmObj, and msg must be the last
723  * arg of function before '...'.
724  */
725 #define SCM_ERROR_MESSAGE_FORMAT(ostr, msg)             \
726     do {                                                \
727         ostr = Scm_MakeOutputStringPort(TRUE);          \
728         va_list args__;                                 \
729         va_start(args__, msg);                          \
730         Scm_Vprintf(SCM_PORT(ostr), msg, args__, TRUE); \
731         va_end(args__);                                 \
732     } while (0)
733 
734 /* Like SCM_ERROR_MESSAGE_FORMAT, but we also append system error message.
735  * Need to pass errno, which should be fetched by get_errno() *before*
736  * calling Scm_VM().
737  */
738 #define SCM_SYSERROR_MESSAGE_FORMAT(ostr, msg, en)      \
739     do {                                                \
740         SCM_ERROR_MESSAGE_FORMAT(ostr, msg);            \
741         ScmObj syserr = get_syserrmsg(en);              \
742         SCM_PUTZ(": ", -1, ostr);                       \
743         SCM_PUTS(syserr, ostr);                         \
744     } while (0)
745 
746 /*
747  * C-like interface
748  */
Scm_Error(const char * msg,...)749 void Scm_Error(const char *msg, ...)
750 {
751     ScmVM *vm = Scm_VM();
752     SCM_ERROR_DOUBLE_FAULT_CHECK(vm, msg);
753     ScmObj ostr;
754     SCM_ERROR_MESSAGE_FORMAT(ostr, msg);
755     ScmObj e = Scm_MakeError(Scm_GetOutputString(SCM_PORT(ostr), TRUE));
756     Scm_VMThrowException(vm, e, SCM_RAISE_NON_CONTINUABLE);
757     Scm_Panic("Scm_Error: Scm_VMThrowException returned.  something wrong.");
758 }
759 
760 /*
761  * Just for convenience to report a system error.   Add strerror() message
762  * after the provided message.
763  */
get_syserrmsg(int en)764 static ScmObj get_syserrmsg(int en)
765 {
766     ScmObj syserr;
767 #if !defined(GAUCHE_WINDOWS)
768     syserr = SCM_MAKE_STR_COPYING(strerror(en));
769 #else  /*GAUCHE_WINDOWS*/
770     if (en < 0) {
771       ScmObj ostr = Scm_MakeOutputStringPort(TRUE);
772       LPTSTR msgbuf = NULL;
773       const char *xmsgbuf;
774       if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
775                         FORMAT_MESSAGE_FROM_SYSTEM |
776                         FORMAT_MESSAGE_IGNORE_INSERTS,
777                         NULL,
778                         -en,
779                         MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
780                         (LPTSTR)&msgbuf,
781                         0, NULL)) {
782         xmsgbuf = SCM_WCS2MBS(msgbuf);
783         SCM_PUTZ(xmsgbuf, -1, ostr);
784       }
785       LocalFree(msgbuf);
786       Scm_Printf(SCM_PORT(ostr), "(error code = %d)", -en);
787       syserr = Scm_GetOutputString(SCM_PORT(ostr), 0);
788     } else {
789       syserr = SCM_MAKE_STR_COPYING(strerror(en));
790     }
791 #endif /*GAUCHE_WINDOWS*/
792     return syserr;
793 }
794 
get_errno(void)795 static int get_errno(void)
796 {
797 #if !defined(GAUCHE_WINDOWS)
798     return errno;
799 #else  /*GAUCHE_WINDOWS*/
800     int en;
801 
802     if (errno == 0) {
803         en = -(int)GetLastError();
804     } else {
805         en = errno;             /* NB: MSDN says we should use _get_errno,
806                                    but MinGW doesn't seem to have it yet.*/
807     }
808 
809     /* Reset the error code, so that we can find which is the actual
810        error code in the next occasion. */
811     errno = 0;                  /* NB: MSDN says we should use _set_errno,
812                                    but MinGW doesn't seem to have it yet. */
813     SetLastError(0);
814 
815     return en;
816 #endif /*GAUCHE_WINDOWS*/
817 }
818 
Scm_SysError(const char * msg,...)819 void Scm_SysError(const char *msg, ...)
820 {
821     int en = get_errno();       /* must take this before Scm_VM() */
822     ScmVM *vm = Scm_VM();
823     SCM_ERROR_DOUBLE_FAULT_CHECK(vm, msg);
824 
825     ScmObj ostr;
826     SCM_SYSERROR_MESSAGE_FORMAT(ostr, msg, en);
827     ScmObj e = Scm_MakeSystemError(Scm_GetOutputString(SCM_PORT(ostr), TRUE), en);
828     Scm_VMThrowException(vm, e, SCM_RAISE_NON_CONTINUABLE);
829     Scm_Panic("Scm_Error: Scm_VMThrowException returned.  something wrong.");
830 }
831 
832 /*
833  * A convenience function to raise argument-type-violation errors.
834  * Right now it raises <error>, but it'll be changed once we adopt R6RS
835  * (The draft R6RS defines 'contract violation' in such cases).
836  */
Scm_TypeError(const char * what,const char * expected,ScmObj got)837 void Scm_TypeError(const char *what, const char *expected, ScmObj got)
838 {
839     Scm_Error("%s is supposed to be of type %s, but got %S",
840               what, expected, got);
841 }
842 
843 
844 /*
845  * A convenience function to raise port-related errors.
846  * It creates either one of port error instance,
847  * depending on the 'reason' argument.
848  * If errno isn't zero, it also creates a <system-error> and throws
849  * a compound condition of both.
850  */
raise_port_error(ScmVM * vm,ScmPort * port,int reason,ScmObj auxinfo,int orig_errno,const char * msg,va_list args)851 void raise_port_error(ScmVM *vm, ScmPort *port, int reason, ScmObj auxinfo,
852                       int orig_errno, const char *msg, va_list args)
853 {
854     Scm_SetPortErrorOccurred(port, TRUE);
855 
856     ScmObj ostr = Scm_MakeOutputStringPort(TRUE);
857     Scm_Vprintf(SCM_PORT(ostr), msg, args, TRUE);
858     ScmObj syserr = get_syserrmsg(orig_errno);
859     Scm_Putz(": ", -1, SCM_PORT(ostr));
860     Scm_Puts(SCM_STRING(syserr), SCM_PORT(ostr));
861     ScmObj smsg = Scm_GetOutputString(SCM_PORT(ostr), TRUE);
862 
863     ScmClass *peclass;
864     switch (reason) {
865     case SCM_PORT_ERROR_INPUT:
866         peclass = SCM_CLASS_IO_READ_ERROR; break;
867     case SCM_PORT_ERROR_OUTPUT:
868         peclass = SCM_CLASS_IO_WRITE_ERROR; break;
869     case SCM_PORT_ERROR_CLOSED:
870         peclass = SCM_CLASS_IO_CLOSED_ERROR; break;
871     case SCM_PORT_ERROR_UNIT:
872         peclass = SCM_CLASS_IO_UNIT_ERROR; break;
873     case SCM_PORT_ERROR_DECODING:
874         peclass = SCM_CLASS_IO_DECODING_ERROR; break;
875     case SCM_PORT_ERROR_ENCODING:
876         peclass = SCM_CLASS_IO_ENCODING_ERROR; break;
877     case SCM_PORT_ERROR_SEEK:
878         peclass = SCM_CLASS_PORT_ERROR; break;
879     case SCM_PORT_ERROR_INVALID_POSITION:
880         peclass = SCM_CLASS_IO_INVALID_POSITION_ERROR; break;
881     default:
882         peclass = SCM_CLASS_PORT_ERROR; break;
883     }
884 
885     ScmObj pe = porterror_allocate(peclass, SCM_NIL);
886     SCM_ERROR(pe)->message = SCM_LIST2(smsg, smsg);
887     SCM_PORT_ERROR(pe)->port = port;
888     SCM_PORT_ERROR(pe)->auxinfo = auxinfo;
889 
890     ScmObj e = pe;
891     if (orig_errno != 0) {
892         e = Scm_MakeCompoundCondition(SCM_LIST2(Scm_MakeSystemError(smsg, orig_errno),
893                                                 pe));
894     }
895     Scm_VMThrowException(vm, e, SCM_RAISE_NON_CONTINUABLE);
896 }
897 
Scm_PortErrorWithAux(ScmPort * port,int reason,ScmObj auxinfo,const char * msg,...)898 void Scm_PortErrorWithAux(ScmPort *port, int reason, ScmObj auxinfo,
899                           const char *msg, ...)
900 {
901     int en = get_errno();       /* must take this before Scm_VM() */
902     ScmVM *vm = Scm_VM();
903     SCM_ERROR_DOUBLE_FAULT_CHECK(vm, msg);
904 
905     va_list ap;
906     va_start(ap, msg);
907     raise_port_error(vm, port, reason, auxinfo, en, msg, ap);
908     va_end(ap);
909     Scm_Panic("Scm_Error: Scm_VMThrowException returned.  something wrong.");
910 }
911 
Scm_PortError(ScmPort * port,int reason,const char * msg,...)912 void Scm_PortError(ScmPort *port, int reason, const char *msg, ...)
913 {
914     int en = get_errno();       /* must take this before Scm_VM() */
915     ScmVM *vm = Scm_VM();
916     SCM_ERROR_DOUBLE_FAULT_CHECK(vm, msg);
917 
918     va_list ap;
919     va_start(ap, msg);
920     raise_port_error(vm, port, reason, SCM_NIL, en, msg, ap);
921     va_end(ap);
922     Scm_Panic("Scm_Error: Scm_VMThrowException returned.  something wrong.");
923 }
924 
925 /*
926  * Just print warning
927  *  TODO: customize behavior
928  */
929 
Scm_Warn(const char * msg,...)930 void Scm_Warn(const char *msg, ...)
931 {
932     if (Scm_GetEnv("GAUCHE_SUPPRESS_WARNING") != NULL) return;
933     va_list args;
934     va_start(args, msg);
935     Scm_Printf(SCM_CURERR, "WARNING: %A\n", Scm_Vsprintf(msg, args, TRUE));
936     Scm_Flush(SCM_CURERR);
937     va_end(args);
938 }
939 
940 /* OBSOLETED: 'warn' is now in Scheme. */
Scm_FWarn(ScmString * fmt SCM_UNUSED,ScmObj args SCM_UNUSED)941 void Scm_FWarn(ScmString *fmt SCM_UNUSED, ScmObj args SCM_UNUSED)
942 {
943     Scm_Error("Scm_FWarn is obsoleted");
944 }
945 
946 /*
947  * General exception raising
948  */
949 
950 /* An external API to hide Scm_VMThrowException. */
Scm_Raise(ScmObj condition,u_long flags)951 ScmObj Scm_Raise(ScmObj condition, u_long flags)
952 {
953     return Scm_VMThrowException(Scm_VM(), condition, flags);
954 }
955 
956 
957 /* A convenient API---allows to call user-defined condition easily,
958    even the condition type is defined in Scheme.  For example:
959 
960    Scm_RaiseCondition(SCM_SYMBOL_VALUE("mymodule", "<my-error>"),
961                       "error-type", SCM_INTERN("fatal"),
962                       "error-code", SCM_MAKE_INT(3),
963                       SCM_RAISE_CONDITION_MESSAGE,
964                       "Fatal error occurred at %S", current_proc);
965 
966    roughly corresponds to the Scheme code:
967 
968    (raise (condition
969             (<my-error> (error-type 'fatal)
970                         (error-code 3)
971                         (message (format "Fatal error occurred at ~s"
972                                          current_proc)))))
973 
974    This function isn't very efficient; but sometimes you want the convenience
975    more, right?
976 
977    The argument list format:
978 
979      <condition-type> {<string-slot-name> <value>}* <terminator>
980 
981      <terminator> : SCM_RAISE_CONDITION_MESSAGE <fmtstr> <fmtarg> ...
982                   | NULL
983 */
984 
Scm_RaiseCondition(ScmObj condition_type,...)985 ScmObj Scm_RaiseCondition(ScmObj condition_type, ...)
986 {
987     ScmObj argh = SCM_NIL, argt = SCM_NIL;
988     va_list ap;
989 
990     if (!SCM_CLASSP(condition_type)
991         || !Scm_SubtypeP(SCM_CLASS(condition_type), SCM_CLASS_CONDITION)) {
992         /* If we don't get a condition type, fallback to a normal error. */
993         condition_type = SCM_OBJ(SCM_CLASS_ERROR);
994     }
995     SCM_APPEND1(argh, argt, condition_type);
996     va_start(ap, condition_type);
997     for (;;) {
998         const char *key = va_arg(ap, const char *);
999         if (key == NULL) {
1000             break;
1001         } else if (key == SCM_RAISE_CONDITION_MESSAGE) {
1002             const char *msg = va_arg(ap, const char*);
1003             ScmObj ostr = Scm_MakeOutputStringPort(TRUE);
1004             Scm_Vprintf(SCM_PORT(ostr), msg, ap, TRUE);
1005             SCM_APPEND1(argh, argt, SCM_MAKE_KEYWORD("message"));
1006             SCM_APPEND1(argh, argt, Scm_GetOutputString(SCM_PORT(ostr), 0));
1007             break;
1008         } else {
1009             ScmObj arg = va_arg(ap, ScmObj);
1010             SCM_APPEND1(argh, argt, SCM_MAKE_KEYWORD(key));
1011             SCM_APPEND1(argh, argt, arg);
1012         }
1013     }
1014     va_end(ap);
1015     return Scm_ApplyRec(SCM_SYMBOL_VALUE("gauche", "error"), argh);
1016 }
1017 
1018 /*
1019  * Show stack or call trace.
1020  *   stacklite - return value of Scm_VMGetStackLite or Scm_VMGetCallTraceLite
1021  *   maxdepth - maximum # of entries to be shown.
1022  *              0 to use the default.  -1 for unlimited.
1023  *   skip     - ignore this number of frames.  Useful to call this from
1024  *              a Scheme error handling routine, in order to skip the
1025  *              frames of the handler itself.
1026  *   offset   - add this to the frame number.  Useful to show a middle part
1027  *              of frames only, by combining the skip parameter.
1028  *   format   - SCM_STACK_TRACE_FORMAT_* enum value.  No longer used.
1029  */
Scm_ShowStackTrace(ScmPort * out,ScmObj stacklite,int maxdepth,int skip,int offset,int format SCM_UNUSED)1030 void Scm_ShowStackTrace(ScmPort *out, ScmObj stacklite,
1031                         int maxdepth, int skip, int offset,
1032                         int format SCM_UNUSED)
1033 {
1034     static ScmObj show_stack_trace = SCM_UNDEFINED;
1035     SCM_BIND_PROC(show_stack_trace,
1036                   "%show-stack-trace",
1037                   Scm_GaucheInternalModule());
1038     Scm_ApplyRec5(show_stack_trace, stacklite, SCM_OBJ(out),
1039                   SCM_MAKE_INT(maxdepth), SCM_MAKE_INT(skip),
1040                   SCM_MAKE_INT(offset));
1041 }
1042 
1043 /* Dump stack trace.  Called from the default error reporter.
1044    Also intended to be called from the debugger, so we allow vm to be NULL
1045    to mean the current VM, and port to be NULL for the current error port. */
Scm_DumpStackTrace(ScmVM * vm,ScmPort * port)1046 void Scm_DumpStackTrace(ScmVM *vm, ScmPort *port)
1047 {
1048     if (vm == NULL) vm = Scm_VM();
1049     if (port == NULL) port = SCM_VM_CURRENT_ERROR_PORT(vm);
1050     ScmObj stack = Scm_VMGetStackLite(vm);
1051     ScmObj calls = Scm_VMGetCallTraceLite(vm);
1052     SCM_PUTZ("Stack Trace:\n", -1, port);
1053     SCM_PUTZ("_______________________________________\n", -1, port);
1054     Scm_ShowStackTrace(port, stack, 0, 0, 0, 0);
1055     if (SCM_PAIRP(calls)) {
1056         SCM_PUTZ("Call Trace:\n", -1, port);
1057         SCM_PUTZ("_______________________________________\n", -1, port);
1058         Scm_ShowStackTrace(port, calls, 0, 0, 0, 0);
1059     }
1060     SCM_FLUSH(port);
1061 }
1062 
1063 /*
1064  * Default error reporter
1065  */
1066 
1067 /* The default procedure to display the header of error message.
1068    E is a thrown condition, not necessarily an error object.
1069 
1070    The actual operation is written in Scheme (libexc.scm).  However,
1071    we can't use that before the infrastructure is fully booted; so
1072    this routine has a fallback which will be used only during initialization.
1073 */
Scm_PrintDefaultErrorHeading(ScmObj e,ScmPort * out)1074 static void Scm_PrintDefaultErrorHeading(ScmObj e, ScmPort *out)
1075 {
1076     if (Scm_InitializedP()) {
1077         static ScmObj print_default_error_heading = SCM_UNDEFINED;
1078         SCM_BIND_PROC(print_default_error_heading,
1079                       "print-default-error-heading",
1080                       Scm_GaucheModule());
1081         Scm_ApplyRec2(print_default_error_heading, e, SCM_OBJ(out));
1082     } else {
1083         /* Error during initialization. */
1084         if (SCM_CONDITIONP(e)) {
1085             Scm_Printf(out, "*** %A: %A\n",
1086                        Scm_ConditionTypeName(e),
1087                        Scm_ConditionMessage(e));
1088         } else {
1089             Scm_Printf(out, "*** ERROR: Unhandled condition: %S\n", e);
1090         }
1091     }
1092 }
1093 
1094 /* We treat out == #f or #t just like 'format' - #t for the current output
1095    port, and #f for string port.  If it's output port, use it.  For any
1096    other objects, we use current error port.  This permissive behavior is
1097    intentional - report-error is usually called during error handling,
1098    and raising an error there masks the original error.  However, the
1099    caller should explicitly pass SCM_OBJ(SCM_CURERR) if that's what it intends,
1100    instead of relying on the permissive behavior.
1101 */
Scm_ReportError(ScmObj e,ScmObj out)1102 ScmObj Scm_ReportError(ScmObj e, ScmObj out)
1103 {
1104     ScmVM *vm = Scm_VM();
1105     ScmPort *port = SCM_VM_CURRENT_ERROR_PORT(vm);
1106     if (SCM_FALSEP(out)) {
1107         port = SCM_PORT(Scm_MakeOutputStringPort(TRUE));
1108     } else if (SCM_TRUEP(out)) {
1109         port = SCM_VM_CURRENT_OUTPUT_PORT(vm);
1110     } else if (SCM_OPORTP(out)) {
1111         port = SCM_PORT(out);
1112     }
1113     Scm_PrintDefaultErrorHeading(e, port);
1114     Scm_DumpStackTrace(vm, port);
1115     if (SCM_FALSEP(out)) return Scm_GetOutputString(SCM_PORT(port), 0);
1116     else return SCM_UNDEFINED;
1117 }
1118 
1119 /*
1120  * Initialization
1121  */
Scm__InitExceptions(void)1122 void Scm__InitExceptions(void)
1123 {
1124     ScmModule *mod = Scm_GaucheModule();
1125 
1126     ScmObj mes_ser_supers
1127         = SCM_LIST2(SCM_OBJ(SCM_CLASS_MESSAGE_CONDITION),
1128                     SCM_OBJ(SCM_CLASS_SERIOUS_CONDITION));
1129     ScmObj com_ser_supers
1130         = SCM_LIST2(SCM_OBJ(SCM_CLASS_COMPOUND_CONDITION),
1131                     SCM_OBJ(SCM_CLASS_SERIOUS_CONDITION));
1132 
1133     Scm_InitStaticClassWithMeta(SCM_CLASS_CONDITION,
1134                                 "<condition>",
1135                                 mod, NULL, SCM_FALSE, NULL, 0);
1136     ScmClass *cond_meta = Scm_ClassOf(SCM_OBJ(SCM_CLASS_CONDITION));
1137     Scm_InitStaticClassWithMeta(SCM_CLASS_SERIOUS_CONDITION,
1138                                 "<serious-condition>",
1139                                 mod, cond_meta, SCM_FALSE, NULL, 0);
1140     Scm_InitStaticClassWithMeta(SCM_CLASS_MESSAGE_CONDITION,
1141                                 "<message-condition>",
1142                                 mod, cond_meta, SCM_FALSE, message_slots, 0);
1143     Scm_InitStaticClassWithMeta(SCM_CLASS_MIXIN_CONDITION,
1144                                 "<mixin-condition>",
1145                                 mod, cond_meta, SCM_FALSE, NULL, 0);
1146 
1147     Scm_InitStaticClassWithMeta(SCM_CLASS_ERROR,
1148                                 "<error>",
1149                                 mod, cond_meta, mes_ser_supers,
1150                                 message_slots, 0);
1151     Scm_InitStaticClassWithMeta(SCM_CLASS_SYSTEM_ERROR,
1152                                 "<system-error>",
1153                                 mod, cond_meta, SCM_FALSE,
1154                                 syserror_slots, 0);
1155     Scm_InitStaticClassWithMeta(SCM_CLASS_UNHANDLED_SIGNAL_ERROR,
1156                                 "<unhandled-signal-error>",
1157                                 mod, cond_meta, SCM_FALSE,
1158                                 sigerror_slots, 0);
1159     Scm_InitStaticClassWithMeta(SCM_CLASS_READ_ERROR,
1160                                 "<read-error>",
1161                                 mod, cond_meta, SCM_FALSE,
1162                                 readerror_slots, 0);
1163     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_ERROR,
1164                                 "<io-error>",
1165                                 mod, cond_meta, SCM_FALSE,
1166                                 NULL, 0);
1167     Scm_InitStaticClassWithMeta(SCM_CLASS_PORT_ERROR,
1168                                 "<port-error>",
1169                                 mod, cond_meta, SCM_FALSE,
1170                                 porterror_slots, 0);
1171     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_READ_ERROR,
1172                                 "<io-read-error>",
1173                                 mod, cond_meta, SCM_FALSE,
1174                                 porterror_slots, 0);
1175     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_WRITE_ERROR,
1176                                 "<io-write-error>",
1177                                 mod, cond_meta, SCM_FALSE,
1178                                 porterror_slots, 0);
1179     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_CLOSED_ERROR,
1180                                 "<io-closed-error>",
1181                                 mod, cond_meta, SCM_FALSE,
1182                                 porterror_slots, 0);
1183     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_UNIT_ERROR,
1184                                 "<io-unit-error>",
1185                                 mod, cond_meta, SCM_FALSE,
1186                                 porterror_slots, 0);
1187     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_DECODING_ERROR,
1188                                 "<io-decoding-error>",
1189                                 mod, cond_meta, SCM_FALSE,
1190                                 porterror_slots, 0);
1191     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_ENCODING_ERROR,
1192                                 "<io-encoding-error>",
1193                                 mod, cond_meta, SCM_FALSE,
1194                                 encodingerror_slots, 0);
1195     Scm_InitStaticClassWithMeta(SCM_CLASS_IO_INVALID_POSITION_ERROR,
1196                                 "<io-invalid-position-error>",
1197                                 mod, cond_meta, SCM_FALSE,
1198                                 invalidpositionerror_slots, 0);
1199 
1200     Scm_InitStaticClassWithMeta(SCM_CLASS_COMPOUND_CONDITION,
1201                                 "<compound-condition>",
1202                                 mod, cond_meta, SCM_FALSE,
1203                                 compound_slots, 0);
1204     Scm_InitStaticClassWithMeta(SCM_CLASS_SERIOUS_COMPOUND_CONDITION,
1205                                 "<serious-compound-condition>",
1206                                 mod, cond_meta, com_ser_supers,
1207                                 compound_slots, 0);
1208 
1209     sym_offending_char = SCM_INTERN("offending-char");
1210     sym_position = SCM_INTERN("position");
1211 }
1212