qr_reader-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-mruby.txt +22 -0
  3. data/LICENSE-spinel.txt +19 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +61 -0
  6. data/ext/qr_reader_native/SPINEL_VERSION +1 -0
  7. data/ext/qr_reader_native/extconf.rb +8 -0
  8. data/ext/qr_reader_native/mruby/error.h +13 -0
  9. data/ext/qr_reader_native/mruby/internal.h +145 -0
  10. data/ext/qr_reader_native/mruby/string.h +22 -0
  11. data/ext/qr_reader_native/mruby.h +93 -0
  12. data/ext/qr_reader_native/mruby_shim.h +118 -0
  13. data/ext/qr_reader_native/qr_reader_native.c +13513 -0
  14. data/ext/qr_reader_native/qr_reader_native.h +29 -0
  15. data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
  16. data/ext/qr_reader_native/re_cased.h +71 -0
  17. data/ext/qr_reader_native/re_compile.c +5784 -0
  18. data/ext/qr_reader_native/re_ctype.h +3540 -0
  19. data/ext/qr_reader_native/re_exec.c +2190 -0
  20. data/ext/qr_reader_native/re_internal.h +671 -0
  21. data/ext/qr_reader_native/re_prop.h +4629 -0
  22. data/ext/qr_reader_native/re_spinel.c +363 -0
  23. data/ext/qr_reader_native/re_spinel.h +22 -0
  24. data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
  25. data/ext/qr_reader_native/re_utf8.c +156 -0
  26. data/ext/qr_reader_native/sp_alloc.c +1447 -0
  27. data/ext/qr_reader_native/sp_alloc.h +1196 -0
  28. data/ext/qr_reader_native/sp_argf.h +33 -0
  29. data/ext/qr_reader_native/sp_array.c +1052 -0
  30. data/ext/qr_reader_native/sp_array.h +456 -0
  31. data/ext/qr_reader_native/sp_bigint.c +5826 -0
  32. data/ext/qr_reader_native/sp_bigint.h +117 -0
  33. data/ext/qr_reader_native/sp_cold.c +5259 -0
  34. data/ext/qr_reader_native/sp_compat.h +402 -0
  35. data/ext/qr_reader_native/sp_core.c +818 -0
  36. data/ext/qr_reader_native/sp_core.h +56 -0
  37. data/ext/qr_reader_native/sp_crypto.c +877 -0
  38. data/ext/qr_reader_native/sp_crypto.h +169 -0
  39. data/ext/qr_reader_native/sp_dtoa.c +1533 -0
  40. data/ext/qr_reader_native/sp_dtoa.h +29 -0
  41. data/ext/qr_reader_native/sp_encoding_names.h +172 -0
  42. data/ext/qr_reader_native/sp_enum.h +86 -0
  43. data/ext/qr_reader_native/sp_exc.c +1185 -0
  44. data/ext/qr_reader_native/sp_exc.h +186 -0
  45. data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
  46. data/ext/qr_reader_native/sp_fiber.c +889 -0
  47. data/ext/qr_reader_native/sp_fiber.h +162 -0
  48. data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
  49. data/ext/qr_reader_native/sp_format.c +695 -0
  50. data/ext/qr_reader_native/sp_format.h +83 -0
  51. data/ext/qr_reader_native/sp_gc.c +1793 -0
  52. data/ext/qr_reader_native/sp_gc.h +762 -0
  53. data/ext/qr_reader_native/sp_hash.c +131 -0
  54. data/ext/qr_reader_native/sp_hash.h +131 -0
  55. data/ext/qr_reader_native/sp_inspect.c +215 -0
  56. data/ext/qr_reader_native/sp_inspect.h +128 -0
  57. data/ext/qr_reader_native/sp_io.c +1676 -0
  58. data/ext/qr_reader_native/sp_io.h +272 -0
  59. data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
  60. data/ext/qr_reader_native/sp_iobuffer.h +167 -0
  61. data/ext/qr_reader_native/sp_marshal.c +487 -0
  62. data/ext/qr_reader_native/sp_marshal.h +60 -0
  63. data/ext/qr_reader_native/sp_net.c +1029 -0
  64. data/ext/qr_reader_native/sp_net.h +205 -0
  65. data/ext/qr_reader_native/sp_pack.c +1254 -0
  66. data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
  67. data/ext/qr_reader_native/sp_proc.c +410 -0
  68. data/ext/qr_reader_native/sp_proc.h +327 -0
  69. data/ext/qr_reader_native/sp_process.c +547 -0
  70. data/ext/qr_reader_native/sp_process_status.c +211 -0
  71. data/ext/qr_reader_native/sp_process_status.h +66 -0
  72. data/ext/qr_reader_native/sp_random.c +287 -0
  73. data/ext/qr_reader_native/sp_random.h +52 -0
  74. data/ext/qr_reader_native/sp_range.h +144 -0
  75. data/ext/qr_reader_native/sp_re.c +1379 -0
  76. data/ext/qr_reader_native/sp_re.h +186 -0
  77. data/ext/qr_reader_native/sp_re_names.h +59 -0
  78. data/ext/qr_reader_native/sp_sched.c +4044 -0
  79. data/ext/qr_reader_native/sp_sched.h +295 -0
  80. data/ext/qr_reader_native/sp_slab.c +1507 -0
  81. data/ext/qr_reader_native/sp_str.c +1462 -0
  82. data/ext/qr_reader_native/sp_str.h +306 -0
  83. data/ext/qr_reader_native/sp_str_crypt.c +23 -0
  84. data/ext/qr_reader_native/sp_string.c +92 -0
  85. data/ext/qr_reader_native/sp_string.h +253 -0
  86. data/ext/qr_reader_native/sp_system.c +78 -0
  87. data/ext/qr_reader_native/sp_system.h +18 -0
  88. data/ext/qr_reader_native/sp_time.c +860 -0
  89. data/ext/qr_reader_native/sp_time.h +100 -0
  90. data/ext/qr_reader_native/sp_types.h +292 -0
  91. data/ext/qr_reader_native/spinel_rt.h +19017 -0
  92. data/ext/qr_reader_native/unicase.c +399 -0
  93. data/ext/qr_reader_native/unicase.h +901 -0
  94. data/lib/qr_reader/native.rb +6 -0
  95. metadata +153 -0
@@ -0,0 +1,1185 @@
1
+ /* sp_exc.c -- cold sp_Exception ops (see sp_exc.h). 0 optcarrot uses. */
2
+ #include "sp_exc.h"
3
+ #include "sp_string.h"
4
+ #include <stdarg.h>
5
+ #include "sp_exc_ctx.h"
6
+ #include <errno.h>
7
+
8
+ /* Check if exception class name `raised` is the same as or a subclass of
9
+ `target`, using both the built-in hierarchy and the user hierarchy callback. */
10
+ const char *const *(*sp_user_exc_modules_fn)(const char *) = 0;
11
+
12
+ /* Errno::EWOULDBLOCK and Errno::EAGAIN are the same class in CRuby, as are the
13
+ IO::EWOULDBLOCKWait* and IO::EAGAINWait* pairs; a name-keyed hierarchy has to
14
+ fold them together or `rescue Errno::EWOULDBLOCK` would miss an EAGAIN. */
15
+ const char *sp_exc_canonical_name(const char *cls) {
16
+ if (!cls) return cls;
17
+ if (!strcmp(cls, "Errno::EWOULDBLOCK")) return SPL("Errno::EAGAIN");
18
+ if (!strcmp(cls, "IO::EWOULDBLOCKWaitReadable")) return SPL("IO::EAGAINWaitReadable");
19
+ if (!strcmp(cls, "IO::EWOULDBLOCKWaitWritable")) return SPL("IO::EAGAINWaitWritable");
20
+ return cls;
21
+ }
22
+
23
+ /* The builtin classes that include a module. Only the exception side needs
24
+ this: class VALUES already walk the module-aware sp_class_ancestors. */
25
+ const char *const *sp_exc_modules_of_name(const char *cls) {
26
+ if (!cls) return 0;
27
+ static const char *const WAIT_R[] = { "IO::WaitReadable", 0 };
28
+ static const char *const WAIT_W[] = { "IO::WaitWritable", 0 };
29
+ if (!strcmp(cls, "IO::EAGAINWaitReadable") ||
30
+ !strcmp(cls, "IO::EINPROGRESSWaitReadable")) return WAIT_R;
31
+ if (!strcmp(cls, "IO::EAGAINWaitWritable") ||
32
+ !strcmp(cls, "IO::EINPROGRESSWaitWritable")) return WAIT_W;
33
+ return 0;
34
+ }
35
+
36
+ /* Does `cls` itself, through any module it includes, answer to `target`? */
37
+ static int sp_exc_level_matches(const char *cls, const char *target) {
38
+ if (!strcmp(cls, target)) return 1;
39
+ const char *const *mods = sp_exc_modules_of_name(cls);
40
+ if (!mods && sp_user_exc_modules_fn) mods = sp_user_exc_modules_fn(cls);
41
+ for (int i = 0; mods && mods[i]; i++)
42
+ if (!strcmp(mods[i], target)) return 1;
43
+ return 0;
44
+ }
45
+
46
+ /* Class names are rodata by contract -- every caller passes a bare literal and
47
+ sp_exc_gc_scan never marks them -- so they take no string root here or in
48
+ the raise path. Rooting one had the collector read its marker byte, which
49
+ for a bare literal is the byte BEFORE the object: an out-of-bounds read on
50
+ every raise, and a WRITE of 0xfc over whatever precedes it whenever that
51
+ byte happened to read as an unmarked heap string. */
52
+ int sp_exc_cls_matches(const char *raised, const char *target) {
53
+ if (!raised || !target) return 0;
54
+ raised = sp_exc_canonical_name(raised);
55
+ target = sp_exc_canonical_name(target);
56
+ /* The builtin hierarchy lives in sp_exc_parent_of_name -- there used to be a
57
+ second copy here, and the two drifted (Errno::* / SystemCallError reached
58
+ only one of them, so `rescue SystemCallError` missed what #is_a? matched). */
59
+ const char *cls = raised;
60
+ for (int depth = 0; depth < 30 && cls; depth++) {
61
+ if (sp_exc_level_matches(cls, target)) return 1;
62
+ const char *parent = NULL;
63
+ /* user hierarchy first */
64
+ if (sp_user_exc_parent_fn) parent = sp_user_exc_parent_fn(cls);
65
+ if (!parent) parent = sp_exc_parent_of_name(cls);
66
+ if (!parent) {
67
+ if (!strcmp(target, "Exception") || !strcmp(target, "Object") || !strcmp(target, "BasicObject")) return 1;
68
+ break;
69
+ }
70
+ cls = parent;
71
+ }
72
+ if (!strcmp(target, "Object") || !strcmp(target, "BasicObject") || !strcmp(target, "Kernel")) return 1;
73
+ return 0;
74
+ }
75
+ /* Which of n class names `raised` reaches first going up its ancestry (the
76
+ class itself first, then its user and builtin parents): the index of that
77
+ name, or -1 when none is an ancestor. Picks the most-derived reopening of
78
+ a builtin exception class that defines a method, the way Ruby's lookup
79
+ does, whatever order the reopenings were written in. */
80
+ int sp_exc_nearest_cls(const char *raised, const char *const *targets, int n) {
81
+ if (!raised) return -1;
82
+ const char *cls = sp_exc_canonical_name(raised);
83
+ for (int depth = 0; depth < 30 && cls; depth++) {
84
+ for (int i = 0; i < n; i++)
85
+ if (sp_exc_level_matches(cls, sp_exc_canonical_name(targets[i]))) return i;
86
+ const char *parent = NULL;
87
+ if (sp_user_exc_parent_fn) parent = sp_user_exc_parent_fn(cls);
88
+ if (!parent) parent = sp_exc_parent_of_name(cls);
89
+ cls = parent;
90
+ }
91
+ for (int i = 0; i < n; i++)
92
+ if (!strcmp(targets[i], "Exception")) return i;
93
+ return -1;
94
+ }
95
+ /* Class-gated introspection accessors (#2753-#2756, #2770): each answers only
96
+ on its CRuby-defining class (walking the name-carried hierarchy) and raises
97
+ NoMethodError elsewhere, matching per-class method definitions. */
98
+ SP_COLD void sp_exc_acc_gate(sp_Exception *e, const char *cls, const char *acc) {SP_GC_ROOT(e);
99
+ if (e && sp_exc_cls_matches(e->cls_name, cls)) return;
100
+ sp_raise_cls("NoMethodError",
101
+ sp_sprintf("undefined method '%s' for %s", acc,
102
+ e ? sp_sprintf("an instance of %s", e->cls_name) : "nil"));
103
+ }
104
+ /* Does `raised` descend from StandardError? Used by a bare `rescue` (no class),
105
+ which catches StandardError and its subclasses only. CRuby's non-StandardError
106
+ branch is a small fixed set of system exceptions; EVERY other exception -- all
107
+ library and user classes -- descends from StandardError. So an unknown class
108
+ (a C-raised package error like JSON::ParserError, or a user class with no
109
+ registered parent) defaults to StandardError; only the listed roots and their
110
+ subclasses answer false. */
111
+ int sp_exc_is_standard_error(const char *raised) {
112
+ if (!raised) return 0;
113
+ static const char *const NONSTD[] = {
114
+ "Exception", "NoMemoryError", "ScriptError", "LoadError",
115
+ "NotImplementedError", "SyntaxError", "SecurityError", "SignalException",
116
+ "Interrupt", "SystemExit", "SystemStackError", "fatal", NULL
117
+ };
118
+ const char *cls = raised;
119
+ for (int depth = 0; depth < 30 && cls; depth++) {
120
+ if (!strcmp(cls, "StandardError")) return 1;
121
+ for (int i = 0; NONSTD[i]; i++)
122
+ if (!strcmp(cls, NONSTD[i])) return 0;
123
+ /* walk user subclasses toward their declared parent; a builtin StandardError
124
+ subclass has no user parent and terminates here, defaulting to true. */
125
+ const char *parent = sp_user_exc_parent_fn ? sp_user_exc_parent_fn(cls) : NULL;
126
+ if (!parent) return 1;
127
+ cls = parent;
128
+ }
129
+ return 1;
130
+ }
131
+ /* the 0xff byte is the frozen-literal marker sp_exc_gc_scan reads at msg[-1];
132
+ the string itself is the empty one that follows it */
133
+ static const char sp_exc_no_msg_storage[] = "\xff" "\0" "\xff";
134
+ const char *const sp_exc_no_msg = sp_exc_no_msg_storage + 1;
135
+ /* the frozen empty sentinel: the second "" of the same storage, behind a
136
+ marker byte of its own (sp_exc_no_msg's NUL, then 0xff) */
137
+ const char *const sp_exc_no_msg_frozen = sp_exc_no_msg_storage + 3;
138
+
139
+ /* SystemCallError#errno reads what SystemCallError#initialize stored: the
140
+ number of the Errno class the exception descends from. An exception built
141
+ under a class of that family gets it here, at construction -- the runtime's
142
+ own raises, a rescue binding rebuilt from a raised class and message, and
143
+ `.new` with no initialize of the program's own in between. (A program's
144
+ initialize that never calls super leaves it nil, as in CRuby: its
145
+ constructor clears it, and the super call sets it.) */
146
+ void sp_exc_syserr_init(sp_Exception *e) {
147
+ sp_int num = 0;
148
+ if (e && sp_syserr_kind(e->cls_name, &num) == SP_SYSERR_NUM) e->xkey = sp_box_int(num);
149
+ }
150
+ /* Create an exception for a `rescue => e` binding: like sp_exc_new but
151
+ also looks up the parent class via the user hierarchy callback. */
152
+ /* The exception's own copy of its message. The length is strlen's: what arrives
153
+ is a bare C string as often as a String (see sp_msg_heapify). */
154
+ const char *sp_exc_msg_counted(const char *m, size_t n) {
155
+ SP_GC_ROOT_STR(m);
156
+ char *r = sp_str_alloc(SP_CMSG_HDR + n);
157
+ memcpy(r, "\xff\xfe" "CM" "\xfd\x01", 6);
158
+ uint32_t n32 = (uint32_t)n;
159
+ memcpy(r + 6, &n32, sizeof n32);
160
+ memcpy(r + SP_CMSG_HDR, m, n);
161
+ return r;
162
+ }
163
+ const char *sp_exc_msg_counted_frozen(const char *m, size_t n) {
164
+ char *r = (char *)sp_exc_msg_counted(m, n);
165
+ r[5] = 0x02;
166
+ return r;
167
+ }
168
+ static const char *sp_exc_msg_copy(const char *m) {
169
+ if (sp_cmsg_p(m)) { /* the counted message decodes to its payload, a NUL kept */
170
+ size_t cn = sp_cmsg_len(m);
171
+ char *r = sp_str_alloc(cn);
172
+ memcpy(r, m + SP_CMSG_HDR, cn);
173
+ /* a frozen String's message is frozen (0xfa: a frozen heap String that
174
+ is collected) */
175
+ if (sp_cmsg_frozen(m)) ((unsigned char *)r)[-1] = 0xfa;
176
+ return r;
177
+ }
178
+ size_t n = strlen(m);
179
+ char *r = sp_str_alloc(n);
180
+ memcpy(r, m, n);
181
+ return r;
182
+ }
183
+ /* A raise's frozen message, counted (sp_exc_msg_given) and with no NUL in
184
+ it, as plain text that is frozen (0xfa): what sp_raise_cls carries in
185
+ the handler stack, so every reader of the raw message (the uncaught
186
+ report, a thread's or fiber's re-raise, the name recovery) reads its
187
+ text, and the rescue's exception keeps the mark (sp_exc_new_for_catch).
188
+ Allocated without a collection's turn, as sp_msg_heapify's copy is. */
189
+ const char *sp_exc_msg_plain(const char *m) {
190
+ if (!sp_cmsg_p(m) || !sp_cmsg_frozen(m)) return m;
191
+ size_t n = sp_cmsg_len(m);
192
+ /* an empty message is the explicit-empty sentinel, as sp_exc_msg_given
193
+ makes it for an unfrozen "" */
194
+ if (n == 0) return sp_exc_no_msg_frozen;
195
+ if (memchr(m + SP_CMSG_HDR, 0, n)) return m;
196
+ char *r = sp_str_alloc_nogc(n);
197
+ memcpy(r, m + SP_CMSG_HDR, n);
198
+ ((unsigned char *)r)[-1] = 0xfa;
199
+ return r;
200
+ }
201
+ sp_Exception *sp_exc_new_for_catch(const char *cls, const char *msg) {
202
+ /* every caller hands a message with a marker byte (a raise's, from the
203
+ handler stack, or sp_exc_msg_given's): a frozen one stays frozen */
204
+ int fz = msg && msg != sp_exc_no_msg && !sp_cmsg_p(msg) &&
205
+ (((const unsigned char *)msg)[-1] == 0xfa || ((const unsigned char *)msg)[-1] == 0xf8);
206
+ if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
207
+ sp_Exception *e = sp_exc_new(cls, msg);
208
+ if (fz && e->msg) ((unsigned char *)e->msg)[-1] = 0xfa;
209
+ if (sp_user_exc_parent_fn) {
210
+ const char *par = sp_user_exc_parent_fn(cls);
211
+ if (par) {
212
+ e->parent_cls_name = par;
213
+ sp_exc_syserr_init(e);
214
+ /* the runtime raises no class of the program, so nothing recorded a
215
+ key or a receiver: KeyError#key raises for one, as in CRuby */
216
+ e->has_key = 0; e->has_recv = 0;
217
+ }
218
+ }
219
+ return e;
220
+ }
221
+ /* Allocate a zeroed exception-subclass struct of `sz` bytes with the base
222
+ {cls_name, parent_cls_name, msg} prefix set, for the degenerate catch path
223
+ where a user subclass with ivars was raised without a carried object
224
+ (#1415). Its ivar fields stay zero (nil/0). msg is the only heap field, so
225
+ the base scan suffices. */
226
+ void *sp_exc_new_sub_sized(size_t sz, const char *cls_name, const char *msg) {if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
227
+ sp_Exception *e = (sp_Exception *)sp_gc_alloc(sz, NULL, sp_exc_gc_scan);
228
+ memset(e, 0, sz);
229
+ e->cls_name = cls_name ? cls_name : "RuntimeError";
230
+ e->result = sp_box_nil(); /* memset left tag 0 (int 0); StopIteration#result wants nil */
231
+ e->xname = sp_box_nil();
232
+ e->xkey = sp_box_nil();
233
+ e->xrecv = sp_box_nil();
234
+ if (sp_user_exc_parent_fn) e->parent_cls_name = sp_user_exc_parent_fn(e->cls_name);
235
+ if (e->parent_cls_name) sp_exc_syserr_init(e);
236
+ /* heap-launder the message (see sp_exc_new); memset left msg NULL, so a GC
237
+ during the copy scans a consistent struct */
238
+ SP_GC_ROOT(e);
239
+ /* an explicitly given message stays, even empty (#3713) */
240
+ e->msg = sp_exc_msg_copy((msg && msg[0]) ? msg
241
+ : (sp_exc_msg_empty_given(msg) ? "" : e->cls_name));
242
+ if (msg == sp_exc_no_msg_frozen) ((unsigned char *)e->msg)[-1] = 0xfa; /* `raise C, ""` */
243
+ /* The copy can collect, and a collection promotes the rooted object it
244
+ is filling: an old holder then receives a young string. Recorded after
245
+ the store, since the allocation would clear a record made before it. */
246
+ sp_gc_wb((void *)e);
247
+ return e;
248
+ }
249
+ void sp_exc_gc_scan(void *p) {
250
+ sp_Exception *e = (sp_Exception *)p;
251
+ if (e->msg) sp_mark_string(e->msg);
252
+ if (e->msg_h) sp_gc_mark(e->msg_h);
253
+ if (e->cause) sp_gc_mark(e->cause);
254
+ sp_mark_rbval(e->result);
255
+ sp_mark_rbval(e->xname);
256
+ sp_mark_rbval(e->xkey);
257
+ sp_mark_rbval(e->xrecv);
258
+ /* backtrace storage: the base sp_Exception's `backtrace` field is
259
+ * mirrored by every user exception subclass struct (codegen emits
260
+ * it in the struct definition for ivar-bearing classes; nivars==0
261
+ * subclasses are typedef'd to sp_Exception). So `e->backtrace` is
262
+ * valid for both shapes and the GC can mark it directly. */
263
+ if (e->backtrace) sp_gc_mark(e->backtrace);
264
+ /* cls_name/parent_cls_name point into rodata -- not GC-managed strings */
265
+ }
266
+
267
+ /* Exception#set_backtrace: replace the stored backtrace with `bt`.
268
+ * The base sp_Exception's `backtrace` field is mirrored by every
269
+ * user exception subclass struct (codegen emits it in the struct
270
+ * definition for ivar-bearing classes; nivars==0 subclasses are
271
+ * typedef'd to sp_Exception). So `e->backtrace` is valid for both
272
+ * shapes, no offset arithmetic needed.
273
+ *
274
+ * The codegen gate at src/codegen_call.c stands down for any user
275
+ * class that defines its own #set_backtrace, so this builtin only
276
+ * runs for the base sp_Exception and for user subclasses that did
277
+ * NOT define their own.
278
+ *
279
+ * CRuby returns the array; the AOT codegen reads the receiver from
280
+ * this return value (the sp_RbVal of the stored array) to satisfy
281
+ * `e.set_backtrace(bt)` in a chain. */
282
+ sp_RbVal sp_Exception_set_backtrace(sp_Exception *e, sp_StrArray *bt) {
283
+ SP_GC_ROOT(e);
284
+ SP_GC_ROOT(bt);
285
+ sp_gc_wb((void *)e); /* the receiver may be old; bt is not */
286
+ e->backtrace = bt;
287
+ return bt ? sp_box_obj(bt, SP_BUILTIN_STR_ARRAY) : sp_box_nil();
288
+ }
289
+ /* cls_name is rodata -- every caller passes a bare literal, and the scan below
290
+ says so and never marks it. Rooting it as a STRING had the collector read
291
+ its marker byte, which for a bare literal is the byte BEFORE the object: a
292
+ global-buffer-overflow on every raise, and a WRITE of 0xfc over whatever
293
+ precedes it whenever that byte happens to read as an unmarked heap string.
294
+ ASAN reports it on the first collection inside any raise. */
295
+ /* The message is copied onto the string heap first, then rooted. The caller's
296
+ pointer cannot be rooted as it stands: a bare C literal -- which every raise
297
+ the runtime and the generated code issue passes -- has no marker byte, so
298
+ sp_mark_string reads the byte BEFORE the object, and WRITES 0xfc over it
299
+ whenever that byte reads as an unmarked heap string. The copy runs with no
300
+ collection in between (sp_msg_heapify), so an unrooted heap message cannot
301
+ be swept out from under it either. The no-message sentinel keeps its
302
+ identity: it is what tells an empty message apart from none at all. */
303
+ sp_Exception *sp_exc_new(const char *cls_name, const char *msg) {if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
304
+ sp_Exception *e = (sp_Exception *)sp_gc_alloc(sizeof(sp_Exception), NULL, sp_exc_gc_scan);
305
+ e->cls_name = cls_name ? cls_name : "RuntimeError";
306
+ e->parent_cls_name = NULL;
307
+ e->msg = NULL; /* set below; scan-safe if the copy triggers a GC */
308
+ e->cause = NULL; /* set all fields explicitly; sp_exc_gc_scan reads cause */
309
+ e->result = sp_box_nil();
310
+ e->xname = sp_box_nil();
311
+ /* An Interrupt carries SIGINT as its #signo however it was constructed --
312
+ including the bare `raise Interrupt` class form (#3039). */
313
+ e->xkey = (e->cls_name && !strcmp(e->cls_name, "Interrupt"))
314
+ ? sp_box_int((sp_int)SIGINT) : sp_box_nil();
315
+ e->xrecv = sp_box_nil();
316
+ /* an Errno class's number (#errno); a class of the program reaches its
317
+ Errno ancestor through sp_exc_new_sub / sp_exc_new_for_catch instead */
318
+ if (!strncmp(e->cls_name, "Errno::", 7) || !strncmp(e->cls_name, "IO::E", 5))
319
+ sp_exc_syserr_init(e);
320
+ e->has_recv = 1; /* cleared by the explicit .new emits that record neither */
321
+ e->has_key = 1;
322
+ /* Launder the message into a GC-heap string: sp_exc_gc_scan marks it via
323
+ the tag byte at msg[-1], which only heap strings carry -- keeping a
324
+ raise site's rodata literal would under-read one byte before it. */
325
+ SP_GC_ROOT(e);
326
+ e->msg = sp_exc_msg_copy((msg && msg[0]) ? msg
327
+ : (sp_exc_msg_empty_given(msg) ? ""
328
+ : (cls_name ? cls_name : "RuntimeError")));
329
+ if (msg == sp_exc_no_msg_frozen) ((unsigned char *)e->msg)[-1] = 0xfa; /* `raise C, ""` */
330
+ sp_gc_wb((void *)e); /* same reason as sp_exc_new_sub_sized */
331
+ return e;
332
+ }
333
+ /* Exception#==: same class and message (CRuby value equality); #equal?
334
+ stays pointer identity at the emit site. */
335
+ sp_bool sp_exc_eq(sp_Exception *a, sp_Exception *b) {
336
+ if (a == b) return 1;
337
+ if (!a || !b) return 0;
338
+ if (strcmp(a->cls_name ? a->cls_name : "", b->cls_name ? b->cls_name : "") != 0) return 0;
339
+ /* Exception#== compares class, the STORED message and the backtrace.
340
+ UncaughtThrowError alone leaves its stored message nil and renders
341
+ "uncaught throw :tag" lazily, so Ruby sees two of them as equal whatever
342
+ the tag. We keep the rendered text in ->msg, so skip it for that class
343
+ (#3098). Backtraces are empty here by design, see docs/limitations.md. */
344
+ if (a->cls_name && strcmp(a->cls_name, "UncaughtThrowError") == 0) return 1;
345
+ { const char *am = sp_exc_msg_text(a), *bm = sp_exc_msg_text(b);
346
+ size_t al = am ? sp_str_byte_len(am) : 0, bl = bm ? sp_str_byte_len(bm) : 0;
347
+ if (!am) am = "";
348
+ if (!bm) bm = "";
349
+ return al == bl && memcmp(am, bm, al) == 0; }
350
+ }
351
+ sp_Exception *sp_exc_new_sub(const char *cls_name, const char *parent_cls, const char *msg) {if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
352
+ sp_Exception *e = sp_exc_new(cls_name, msg); /* empty msg already fell back to cls_name */
353
+ e->parent_cls_name = parent_cls;
354
+ sp_exc_syserr_init(e);
355
+ /* `.new` records no key or receiver (KeyError#key raises for it) */
356
+ e->has_key = 0; e->has_recv = 0;
357
+ return e;
358
+ }
359
+ /* Exception#dup / #clone: a fresh allocation of the receiver's full
360
+ (subclass-sized) payload -- the GC header carries the size, so subclass
361
+ ivar fields copy along (as references, matching Object#dup). */
362
+ sp_Exception *sp_exc_dup(sp_Exception *e) {
363
+ if (!e) return e;
364
+ sp_gc_hdr *h = (sp_gc_hdr *)((char *)e - sizeof(sp_gc_hdr));
365
+ size_t payload = h->size - sizeof(sp_gc_hdr);
366
+ SP_GC_ROOT(e);
367
+ sp_Exception *n = (sp_Exception *)sp_gc_alloc(payload, h->finalize, h->scan);
368
+ memcpy(n, e, payload);
369
+ return n;
370
+ }
371
+ /* Write the staged introspection values (receiver/key/value) into the carried
372
+ exception, creating one when the raise had none (see sp_raise_cls). */
373
+ void *sp_exc_apply_staged(const char *cls, const char *msg, void *obj) {if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
374
+ sp_Exception *e = (sp_Exception *)obj;
375
+ if (!e) e = sp_exc_new(cls, msg);
376
+ /* `obj` is a caller-supplied exception that may have been promoted long ago
377
+ (`raise SomeError` on a constant instance, a re-raised one), and the three
378
+ stores below put young values into it. */
379
+ sp_gc_wb((void *)e);
380
+ if (sp_pending_exc_flags & 1) { e->xrecv = sp_pending_exc_recv; e->has_recv = 1; }
381
+ if (sp_pending_exc_flags & 2) { e->xkey = sp_pending_exc_key; e->has_key = 1; }
382
+ if (sp_pending_exc_flags & 4) e->result = sp_pending_exc_val;
383
+ return e;
384
+ }
385
+ /* SystemExit#status carried in the result slot; 0 when unset. */
386
+ int sp_exc_exit_status(void *obj) {
387
+ sp_Exception *e = (sp_Exception *)obj;
388
+ return (e && e->result.tag == SP_TAG_INT) ? (int)e->result.v.i : 0;
389
+ }
390
+ /* Exception#exception(msg): a copy of the receiver carrying the new message. */
391
+ sp_Exception *sp_exc_exception(sp_Exception *e, const char *msg) {SP_GC_ROOT(e);if (!sp_exc_msg_empty_given(msg)) msg = sp_msg_heapify(msg); SP_GC_ROOT_STR(msg);
392
+ sp_Exception *n = sp_exc_dup(e);
393
+ SP_GC_ROOT(n);
394
+ /* an explicitly given empty message stays empty, frozen for a literal's,
395
+ as sp_exc_new keeps it */
396
+ n->msg = sp_exc_msg_copy((msg && msg[0]) ? msg
397
+ : (sp_exc_msg_empty_given(msg) ? "" : (n->cls_name ? n->cls_name : "RuntimeError")));
398
+ if (msg == sp_exc_no_msg_frozen) ((unsigned char *)n->msg)[-1] = 0xfa;
399
+ n->msg_h = NULL; /* the new message, not the receiver's handle */
400
+ sp_gc_wb((void *)n); /* same reason as sp_exc_new_sub_sized */
401
+ return n;
402
+ }
403
+ /* Accept `volatile` pointers: LV slots holding sp_Exception * are
404
+ declared volatile when they live across setjmp, so callers may
405
+ pass volatile-qualified pointers in. The pointee itself isn't
406
+ volatile (cls_name/msg are stable post-construction), so we
407
+ strip volatile internally for one access. */
408
+ const char *sp_exc_class_name(volatile sp_Exception *ve) {
409
+ sp_Exception *e = (sp_Exception *)ve;
410
+ /* cls_name points into rodata (see sp_exc_gc_scan) and it comes from the
411
+ raise site's bare literal, so it carries no marker byte. This name reaches
412
+ Ruby as `e.class.to_s`, where the caller roots it and the collector reads
413
+ that byte -- hand back a marked copy instead. One copy is made for a name
414
+ and kept, as a literal is. A Class value carries its name in a plain
415
+ struct that no root and no scan knows (`k = e.class` held in a local, a
416
+ parameter or an instance variable), so a copy on the string heap was
417
+ collected under it and `k.to_s` answered the String that took its place. */
418
+ struct kept_name { const char *key, *copy; };
419
+ static struct kept_name *kept = NULL; /* guarded by the heap lock */
420
+ static size_t n = 0, cap = 0; /* cap is a power of two; open addressing */
421
+ const char *name, *r = NULL;
422
+ if (!e || !e->cls_name) return SPL("RuntimeError");
423
+ name = e->cls_name;
424
+ /* A name with no header is a literal, or a copy kept here: its address
425
+ names it, and the table is keyed by address. A heap name (a class
426
+ raised across threads) is found by its text. */
427
+ int by_addr = !sp_str_has_hdr(name);
428
+ #define SP_KEPT_SLOT(p) ((size_t)(((uint64_t)(uintptr_t)(p) * 0x9E3779B97F4A7C15ull) >> 40) & (cap - 1))
429
+ SP_HEAP_LOCK();
430
+ if (by_addr && cap)
431
+ for (size_t h = SP_KEPT_SLOT(name); kept[h].key && !r; h = (h + 1) & (cap - 1))
432
+ if (kept[h].key == name) r = kept[h].copy;
433
+ if (!r) {
434
+ /* The first sight of this address. Room for it first, so that a copy
435
+ made is a copy kept. */
436
+ if ((n + 1) * 2 > cap) {
437
+ size_t oc = cap, nc = cap ? cap * 2 : 16;
438
+ struct kept_name *ok = kept, *nk = (struct kept_name *)calloc(nc, sizeof *nk);
439
+ if (nk) {
440
+ kept = nk; cap = nc;
441
+ for (size_t i = 0; i < oc; i++) if (ok[i].key) {
442
+ size_t h = SP_KEPT_SLOT(ok[i].key);
443
+ while (kept[h].key) h = (h + 1) & (cap - 1);
444
+ kept[h] = ok[i];
445
+ }
446
+ free(ok);
447
+ }
448
+ }
449
+ if ((n + 1) * 2 <= cap) {
450
+ /* the same name may be kept already, written at another address */
451
+ const char *copy = NULL;
452
+ for (size_t i = 0; i < cap && !copy; i++)
453
+ if (kept[i].key && !strcmp(kept[i].copy, name)) copy = kept[i].copy;
454
+ int made = 0;
455
+ if (!copy) {
456
+ size_t len = strlen(name);
457
+ char *m = (char *)malloc(len + 2);
458
+ if (m) { m[0] = (char)0xff; memcpy(m + 1, name, len + 1); copy = m + 1; made = 1; }
459
+ }
460
+ /* keyed by this address from now on; a heap name's address is no key,
461
+ so a copy made for one is keyed by itself (`raise k` hands it back) */
462
+ if (copy && (by_addr || made)) {
463
+ size_t h = SP_KEPT_SLOT(by_addr ? name : copy);
464
+ while (kept[h].key) h = (h + 1) & (cap - 1);
465
+ kept[h].key = by_addr ? name : copy; kept[h].copy = copy; n++;
466
+ }
467
+ r = copy;
468
+ }
469
+ }
470
+ SP_HEAP_UNLOCK();
471
+ #undef SP_KEPT_SLOT
472
+ return r ? r : sp_str_dup_external(e->cls_name); /* out of memory */
473
+ }
474
+ /* A copy of a message handle's String as it is now: #message answers a
475
+ String a caller keeps, and the handle's buffer moves as it grows. Sized
476
+ by the handle's length, so a NUL inside stays. */
477
+ static const char *sp_exc_msg_handle_copy(sp_String *h) {
478
+ int64_t n = sp_String_length(h);
479
+ char *r = sp_str_alloc((size_t)n);
480
+ memcpy(r, sp_String_cstr(h), (size_t)n);
481
+ return r;
482
+ }
483
+ const char *sp_exc_msg_text(volatile sp_Exception *ve) {
484
+ sp_Exception *e = (sp_Exception *)ve;
485
+ if (!e) return NULL;
486
+ return e->msg_h ? sp_String_cstr((sp_String *)e->msg_h) : e->msg;
487
+ }
488
+ void *sp_exc_attach_msg(void *p, void *h) {
489
+ sp_Exception *e = (sp_Exception *)p;
490
+ if (e && h) { e->msg_h = h; sp_gc_wb((void *)e); }
491
+ return p;
492
+ }
493
+ const char *sp_exc_message(volatile sp_Exception *ve) {
494
+ sp_Exception *e = (sp_Exception *)ve;
495
+ /* a message never set (super(nil), Exception.new) defaults to the class
496
+ name, as CRuby's Exception#message does */
497
+ if (!e) return sp_str_empty;
498
+ if (e->msg_h) return sp_exc_msg_handle_copy((sp_String *)e->msg_h);
499
+ if (e->msg) return e->msg;
500
+ return e->cls_name ? sp_str_dup_external(e->cls_name) : sp_str_empty;
501
+ }
502
+ /* Exception#cause: the exception active when this one was raised, or NULL. */
503
+ sp_Exception *sp_exc_cause(volatile sp_Exception *ve) {
504
+ sp_Exception *e = (sp_Exception *)ve;
505
+ return e ? e->cause : NULL;
506
+ }
507
+ /* StopIteration#result: the value the finished iteration returned (nil for a
508
+ non-StopIteration exception or a past-the-end materialized enumerator). */
509
+ sp_RbVal sp_exc_result(volatile sp_Exception *ve) {
510
+ sp_Exception *e = (sp_Exception *)ve;
511
+ return e ? e->result : sp_box_nil();
512
+ }
513
+ /* The Errno:: family by number. One table both ways: a raise site picks the
514
+ class for the errno it read, and SystemCallError#errno recovers the number
515
+ from the class name the exception carries -- no field on the exception, as
516
+ in CRuby the class determines the number (#4560). The names are the ones
517
+ the runtime raises today plus the common POSIX rest; an errno with no row
518
+ raises the parent SystemCallError, and a class with no row answers nil.
519
+ Rows go in CRuby's order: where two names share a number, the first is the
520
+ class an errno raises and the other is the same class (the compiler spells
521
+ it the first way, errno_canonical_name), so ENOTSUP precedes EOPNOTSUPP. */
522
+ #define SP_ERRNO_ROWS(X) \
523
+ X(EPERM) X(ENOENT) X(ESRCH) X(EINTR) X(EIO) X(ENXIO) X(E2BIG) X(ENOEXEC) \
524
+ X(EBADF) X(ECHILD) X(EAGAIN) X(ENOMEM) X(EACCES) X(EFAULT) X(EBUSY) \
525
+ X(EEXIST) X(EXDEV) X(ENODEV) X(ENOTDIR) X(EISDIR) X(EINVAL) X(ENFILE) \
526
+ X(EMFILE) X(ENOTTY) X(EFBIG) X(ENOSPC) X(ESPIPE) X(EROFS) X(EMLINK) \
527
+ X(EPIPE) X(EDOM) X(ERANGE) X(EDEADLK) X(ENAMETOOLONG) X(ENOLCK) X(ENOSYS) \
528
+ X(ENOTEMPTY) X(ELOOP) X(ENOTSOCK) X(EMSGSIZE) X(EPROTOTYPE) \
529
+ X(ENOPROTOOPT) X(EPROTONOSUPPORT) X(ENOTSUP) X(EOPNOTSUPP) X(EAFNOSUPPORT) \
530
+ X(EADDRINUSE) X(EADDRNOTAVAIL) X(ENETDOWN) X(ENETUNREACH) X(ENETRESET) \
531
+ X(ECONNABORTED) X(ECONNRESET) X(ENOBUFS) X(EISCONN) X(ENOTCONN) \
532
+ X(ETIMEDOUT) X(ECONNREFUSED) X(EHOSTUNREACH) X(EALREADY) X(EINPROGRESS) \
533
+ X(ESTALE) X(EDQUOT) X(ECANCELED) X(EOVERFLOW) X(EILSEQ)
534
+ static const struct { const char *name; int num; } SP_ERRNO_TAB[] = {
535
+ #define SP_ERRNO_ROW(n) { "Errno::" #n, n },
536
+ SP_ERRNO_ROWS(SP_ERRNO_ROW)
537
+ #undef SP_ERRNO_ROW
538
+ /* names a platform may give a number of their own */
539
+ #ifdef EWOULDBLOCK
540
+ { "Errno::EWOULDBLOCK", EWOULDBLOCK },
541
+ #endif
542
+ #ifdef EDEADLOCK
543
+ { "Errno::EDEADLOCK", EDEADLOCK },
544
+ #endif
545
+ /* the rest of CRuby's Errno classes, each where the platform has it: a
546
+ number with no row here would build a plain SystemCallError where CRuby
547
+ builds its Errno class (SystemCallError.new(msg, n)) */
548
+ #ifdef ENOTBLK
549
+ { "Errno::ENOTBLK", ENOTBLK },
550
+ #endif
551
+ #ifdef ETXTBSY
552
+ { "Errno::ETXTBSY", ETXTBSY },
553
+ #endif
554
+ #ifdef EDESTADDRREQ
555
+ { "Errno::EDESTADDRREQ", EDESTADDRREQ },
556
+ #endif
557
+ #ifdef ESOCKTNOSUPPORT
558
+ { "Errno::ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
559
+ #endif
560
+ #ifdef EPFNOSUPPORT
561
+ { "Errno::EPFNOSUPPORT", EPFNOSUPPORT },
562
+ #endif
563
+ #ifdef ESHUTDOWN
564
+ { "Errno::ESHUTDOWN", ESHUTDOWN },
565
+ #endif
566
+ #ifdef ETOOMANYREFS
567
+ { "Errno::ETOOMANYREFS", ETOOMANYREFS },
568
+ #endif
569
+ #ifdef EHOSTDOWN
570
+ { "Errno::EHOSTDOWN", EHOSTDOWN },
571
+ #endif
572
+ #ifdef EPROCLIM
573
+ { "Errno::EPROCLIM", EPROCLIM },
574
+ #endif
575
+ #ifdef EUSERS
576
+ { "Errno::EUSERS", EUSERS },
577
+ #endif
578
+ #ifdef EREMOTE
579
+ { "Errno::EREMOTE", EREMOTE },
580
+ #endif
581
+ #ifdef EBADRPC
582
+ { "Errno::EBADRPC", EBADRPC },
583
+ #endif
584
+ #ifdef ERPCMISMATCH
585
+ { "Errno::ERPCMISMATCH", ERPCMISMATCH },
586
+ #endif
587
+ #ifdef EPROGUNAVAIL
588
+ { "Errno::EPROGUNAVAIL", EPROGUNAVAIL },
589
+ #endif
590
+ #ifdef EPROGMISMATCH
591
+ { "Errno::EPROGMISMATCH", EPROGMISMATCH },
592
+ #endif
593
+ #ifdef EPROCUNAVAIL
594
+ { "Errno::EPROCUNAVAIL", EPROCUNAVAIL },
595
+ #endif
596
+ #ifdef EFTYPE
597
+ { "Errno::EFTYPE", EFTYPE },
598
+ #endif
599
+ #ifdef EAUTH
600
+ { "Errno::EAUTH", EAUTH },
601
+ #endif
602
+ #ifdef ENEEDAUTH
603
+ { "Errno::ENEEDAUTH", ENEEDAUTH },
604
+ #endif
605
+ #ifdef EPWROFF
606
+ { "Errno::EPWROFF", EPWROFF },
607
+ #endif
608
+ #ifdef EDEVERR
609
+ { "Errno::EDEVERR", EDEVERR },
610
+ #endif
611
+ #ifdef EBADEXEC
612
+ { "Errno::EBADEXEC", EBADEXEC },
613
+ #endif
614
+ #ifdef EBADARCH
615
+ { "Errno::EBADARCH", EBADARCH },
616
+ #endif
617
+ #ifdef ESHLIBVERS
618
+ { "Errno::ESHLIBVERS", ESHLIBVERS },
619
+ #endif
620
+ #ifdef EBADMACHO
621
+ { "Errno::EBADMACHO", EBADMACHO },
622
+ #endif
623
+ #ifdef EIDRM
624
+ { "Errno::EIDRM", EIDRM },
625
+ #endif
626
+ #ifdef ENOMSG
627
+ { "Errno::ENOMSG", ENOMSG },
628
+ #endif
629
+ #ifdef ENOATTR
630
+ { "Errno::ENOATTR", ENOATTR },
631
+ #endif
632
+ #ifdef EBADMSG
633
+ { "Errno::EBADMSG", EBADMSG },
634
+ #endif
635
+ #ifdef EMULTIHOP
636
+ { "Errno::EMULTIHOP", EMULTIHOP },
637
+ #endif
638
+ #ifdef ENODATA
639
+ { "Errno::ENODATA", ENODATA },
640
+ #endif
641
+ #ifdef ENOLINK
642
+ { "Errno::ENOLINK", ENOLINK },
643
+ #endif
644
+ #ifdef ENOSR
645
+ { "Errno::ENOSR", ENOSR },
646
+ #endif
647
+ #ifdef ENOSTR
648
+ { "Errno::ENOSTR", ENOSTR },
649
+ #endif
650
+ #ifdef EPROTO
651
+ { "Errno::EPROTO", EPROTO },
652
+ #endif
653
+ #ifdef ETIME
654
+ { "Errno::ETIME", ETIME },
655
+ #endif
656
+ #ifdef ENOPOLICY
657
+ { "Errno::ENOPOLICY", ENOPOLICY },
658
+ #endif
659
+ #ifdef ENOTRECOVERABLE
660
+ { "Errno::ENOTRECOVERABLE", ENOTRECOVERABLE },
661
+ #endif
662
+ #ifdef EOWNERDEAD
663
+ { "Errno::EOWNERDEAD", EOWNERDEAD },
664
+ #endif
665
+ #ifdef EQFULL
666
+ { "Errno::EQFULL", EQFULL },
667
+ #endif
668
+ #ifdef ECHRNG
669
+ { "Errno::ECHRNG", ECHRNG },
670
+ #endif
671
+ #ifdef EL2NSYNC
672
+ { "Errno::EL2NSYNC", EL2NSYNC },
673
+ #endif
674
+ #ifdef EL3HLT
675
+ { "Errno::EL3HLT", EL3HLT },
676
+ #endif
677
+ #ifdef EL3RST
678
+ { "Errno::EL3RST", EL3RST },
679
+ #endif
680
+ #ifdef ELNRNG
681
+ { "Errno::ELNRNG", ELNRNG },
682
+ #endif
683
+ #ifdef EUNATCH
684
+ { "Errno::EUNATCH", EUNATCH },
685
+ #endif
686
+ #ifdef ENOCSI
687
+ { "Errno::ENOCSI", ENOCSI },
688
+ #endif
689
+ #ifdef EL2HLT
690
+ { "Errno::EL2HLT", EL2HLT },
691
+ #endif
692
+ #ifdef EBADE
693
+ { "Errno::EBADE", EBADE },
694
+ #endif
695
+ #ifdef EBADR
696
+ { "Errno::EBADR", EBADR },
697
+ #endif
698
+ #ifdef EXFULL
699
+ { "Errno::EXFULL", EXFULL },
700
+ #endif
701
+ #ifdef ENOANO
702
+ { "Errno::ENOANO", ENOANO },
703
+ #endif
704
+ #ifdef EBADRQC
705
+ { "Errno::EBADRQC", EBADRQC },
706
+ #endif
707
+ #ifdef EBADSLT
708
+ { "Errno::EBADSLT", EBADSLT },
709
+ #endif
710
+ #ifdef EBFONT
711
+ { "Errno::EBFONT", EBFONT },
712
+ #endif
713
+ #ifdef ENONET
714
+ { "Errno::ENONET", ENONET },
715
+ #endif
716
+ #ifdef ENOPKG
717
+ { "Errno::ENOPKG", ENOPKG },
718
+ #endif
719
+ #ifdef EADV
720
+ { "Errno::EADV", EADV },
721
+ #endif
722
+ #ifdef ESRMNT
723
+ { "Errno::ESRMNT", ESRMNT },
724
+ #endif
725
+ #ifdef ECOMM
726
+ { "Errno::ECOMM", ECOMM },
727
+ #endif
728
+ #ifdef EDOTDOT
729
+ { "Errno::EDOTDOT", EDOTDOT },
730
+ #endif
731
+ #ifdef ENOTUNIQ
732
+ { "Errno::ENOTUNIQ", ENOTUNIQ },
733
+ #endif
734
+ #ifdef EBADFD
735
+ { "Errno::EBADFD", EBADFD },
736
+ #endif
737
+ #ifdef EREMCHG
738
+ { "Errno::EREMCHG", EREMCHG },
739
+ #endif
740
+ #ifdef ELIBACC
741
+ { "Errno::ELIBACC", ELIBACC },
742
+ #endif
743
+ #ifdef ELIBBAD
744
+ { "Errno::ELIBBAD", ELIBBAD },
745
+ #endif
746
+ #ifdef ELIBSCN
747
+ { "Errno::ELIBSCN", ELIBSCN },
748
+ #endif
749
+ #ifdef ELIBMAX
750
+ { "Errno::ELIBMAX", ELIBMAX },
751
+ #endif
752
+ #ifdef ELIBEXEC
753
+ { "Errno::ELIBEXEC", ELIBEXEC },
754
+ #endif
755
+ #ifdef ERESTART
756
+ { "Errno::ERESTART", ERESTART },
757
+ #endif
758
+ #ifdef ESTRPIPE
759
+ { "Errno::ESTRPIPE", ESTRPIPE },
760
+ #endif
761
+ #ifdef EUCLEAN
762
+ { "Errno::EUCLEAN", EUCLEAN },
763
+ #endif
764
+ #ifdef ENOTNAM
765
+ { "Errno::ENOTNAM", ENOTNAM },
766
+ #endif
767
+ #ifdef ENAVAIL
768
+ { "Errno::ENAVAIL", ENAVAIL },
769
+ #endif
770
+ #ifdef EISNAM
771
+ { "Errno::EISNAM", EISNAM },
772
+ #endif
773
+ #ifdef EREMOTEIO
774
+ { "Errno::EREMOTEIO", EREMOTEIO },
775
+ #endif
776
+ #ifdef ENOMEDIUM
777
+ { "Errno::ENOMEDIUM", ENOMEDIUM },
778
+ #endif
779
+ #ifdef EMEDIUMTYPE
780
+ { "Errno::EMEDIUMTYPE", EMEDIUMTYPE },
781
+ #endif
782
+ #ifdef ENOKEY
783
+ { "Errno::ENOKEY", ENOKEY },
784
+ #endif
785
+ #ifdef EKEYEXPIRED
786
+ { "Errno::EKEYEXPIRED", EKEYEXPIRED },
787
+ #endif
788
+ #ifdef EKEYREVOKED
789
+ { "Errno::EKEYREVOKED", EKEYREVOKED },
790
+ #endif
791
+ #ifdef EKEYREJECTED
792
+ { "Errno::EKEYREJECTED", EKEYREJECTED },
793
+ #endif
794
+ #ifdef ERFKILL
795
+ { "Errno::ERFKILL", ERFKILL },
796
+ #endif
797
+ #ifdef EHWPOISON
798
+ { "Errno::EHWPOISON", EHWPOISON },
799
+ #endif
800
+ #ifdef EIPSEC
801
+ { "Errno::EIPSEC", EIPSEC },
802
+ #endif
803
+ #ifdef EDOOFUS
804
+ { "Errno::EDOOFUS", EDOOFUS },
805
+ #endif
806
+ #ifdef ECAPMODE
807
+ { "Errno::ECAPMODE", ECAPMODE },
808
+ #endif
809
+ #ifdef ENOTCAPABLE
810
+ { "Errno::ENOTCAPABLE", ENOTCAPABLE },
811
+ #endif
812
+ };
813
+ const char *sp_errno_class_name(int e) {
814
+ for (size_t i = 0; i < sizeof SP_ERRNO_TAB / sizeof SP_ERRNO_TAB[0]; i++)
815
+ if (SP_ERRNO_TAB[i].num == e) return SP_ERRNO_TAB[i].name;
816
+ return "SystemCallError";
817
+ }
818
+ /* Errno::ENOENT::Errno: the number the class name stands for */
819
+ sp_int sp_errno_num(const char *cls) {
820
+ for (size_t i = 0; i < sizeof SP_ERRNO_TAB / sizeof SP_ERRNO_TAB[0]; i++)
821
+ if (!strcmp(SP_ERRNO_TAB[i].name, cls)) return SP_ERRNO_TAB[i].num;
822
+ /* a name the platform has no number for is CRuby's Errno 0 class */
823
+ return strncmp(cls, "Errno::", 7) ? SP_INT_NIL : 0;
824
+ }
825
+ /* Where class `cls` stands in the SystemCallError family, walking its user
826
+ and builtin parents: SP_SYSERR_NUM when it is (or descends from) an Errno
827
+ class, whose number goes to *num; SP_SYSERR_BASE for SystemCallError
828
+ itself; SP_SYSERR_BARE for a class of the program directly under
829
+ SystemCallError, which has no Errno constant of its own -- CRuby's
830
+ SystemCallError#initialize reads the constant through the class, finds the
831
+ Errno MODULE and raises TypeError converting it; SP_SYSERR_NONE off the
832
+ family. */
833
+ int sp_syserr_kind(const char *cls, sp_int *num) {
834
+ const char *cn = sp_exc_canonical_name(cls);
835
+ for (int depth = 0; depth < 30 && cn; depth++) {
836
+ /* a class of the program named into Errno (`class Errno::Mine <
837
+ Errno::ENOENT`) reads its parent's number, as CRuby reads the
838
+ inherited Errno constant */
839
+ const char *uparent = sp_user_exc_parent_fn ? sp_user_exc_parent_fn(cn) : NULL;
840
+ if (!strncmp(cn, "Errno::", 7) && !uparent) {
841
+ /* a name this platform has no number for: CRuby still defines the
842
+ class, with Errno 0 */
843
+ sp_int n = sp_errno_num(cn);
844
+ if (num) *num = n == SP_INT_NIL ? 0 : n;
845
+ return SP_SYSERR_NUM;
846
+ }
847
+ if (!strcmp(cn, "SystemCallError")) return depth == 0 ? SP_SYSERR_BASE : SP_SYSERR_BARE;
848
+ cn = sp_exc_canonical_name(uparent ? uparent : sp_exc_parent_of_name(cn));
849
+ }
850
+ return SP_SYSERR_NONE;
851
+ }
852
+ /* SystemCallError#message as CRuby's rb_syserr_initialize builds it: the
853
+ C library's text for the number ("unknown error" without one), then
854
+ " @ func" and " - msg" for the arguments given. strerror is what CRuby
855
+ asks too, so the text is the platform's own. `msg` is NULL when none was
856
+ given (nil) -- an empty one is given, and keeps its " - ". */
857
+ const char *sp_syserr_text(int has_num, sp_int num, const char *func, const char *msg) {
858
+ /* one allocation: the caller holds func and msg */
859
+ return sp_sprintf("%s%s%s%s%s", has_num ? strerror((int)num) : "unknown error",
860
+ func ? " @ " : "", func ? func : "", msg ? " - " : "", msg ? msg : "");
861
+ }
862
+ /* SystemCallError#errno: what SystemCallError#initialize stored -- the
863
+ number of the Errno class the exception descends from (sp_exc_syserr_init),
864
+ or what a plain SystemCallError.new(msg, n) was given -- nil when it never
865
+ ran; NoMethodError off the family, as CRuby defines the reader on
866
+ SystemCallError alone. */
867
+ sp_RbVal sp_exc_errno_acc(sp_Exception *e) {SP_GC_ROOT(e);
868
+ sp_exc_acc_gate(e, "SystemCallError", "errno");
869
+ return e->xkey;
870
+ }
871
+ /* The builtin exception hierarchy, as {class, direct superclass} pairs. Shared
872
+ by Exception#is_a? and the by-name #superclass lookup (#3031). */
873
+ const char *sp_exc_parent_of_name(const char *cls) {
874
+ if (!cls) return NULL;
875
+ static const char *const HIER[][2] = {
876
+ {"RuntimeError", "StandardError"},
877
+ {"ArgumentError", "StandardError"},
878
+ {"TypeError", "StandardError"},
879
+ {"NameError", "StandardError"},
880
+ {"NoMethodError", "NameError"},
881
+ {"IndexError", "StandardError"},
882
+ {"KeyError", "IndexError"},
883
+ {"RangeError", "StandardError"},
884
+ {"IOError", "StandardError"},
885
+ {"EOFError", "IOError"},
886
+ {"ZeroDivisionError", "StandardError"},
887
+ {"NotImplementedError", "ScriptError"},
888
+ {"StopIteration", "IndexError"},
889
+ {"FloatDomainError", "RangeError"},
890
+ {"Math::DomainError", "StandardError"},
891
+ {"FrozenError", "RuntimeError"},
892
+ {"EncodingError", "StandardError"},
893
+ {"Encoding::UndefinedConversionError", "EncodingError"},
894
+ {"Encoding::InvalidByteSequenceError", "EncodingError"},
895
+ {"Encoding::CompatibilityError", "EncodingError"},
896
+ /* a LoadError is a ScriptError: `rescue StandardError` and `rescue => e`
897
+ let it through, as sp_exc_is_standard_error already said */
898
+ {"LoadError", "ScriptError"},
899
+ {"RegexpError", "StandardError"},
900
+ {"StringScanner_Error", "StandardError"},
901
+ /* the json package raises these by name; NestingError is what a document
902
+ too deep to serialize raises, and `rescue JSON::ParserError` catches it
903
+ in CRuby because it is a ParserError */
904
+ {"JSON::NestingError", "JSON::ParserError"},
905
+ /* the json package's errors descend from StandardError through JSONError, as
906
+ CRuby's do: an explicit `rescue StandardError` and `is_a?(StandardError)` say so (#7797) */
907
+ {"JSON::JSONError", "StandardError"},
908
+ {"JSON::ParserError", "JSON::JSONError"},
909
+ /* a Float JSON has no spelling for (Infinity, NaN) is refused with it */
910
+ {"JSON::GeneratorError", "JSON::JSONError"},
911
+ /* IO::Buffer's errors (lib/sp_iobuffer.c raises them by name): all
912
+ RuntimeError subclasses in CRuby, except MaskError < ArgumentError */
913
+ {"IO::Buffer::AccessError", "RuntimeError"},
914
+ {"IO::Buffer::LockedError", "RuntimeError"},
915
+ {"IO::Buffer::AllocationError", "RuntimeError"},
916
+ {"IO::Buffer::InvalidatedError", "RuntimeError"},
917
+ {"IO::Buffer::MaskError", "ArgumentError"},
918
+ {"FiberError", "StandardError"},
919
+ {"UncaughtThrowError", "ArgumentError"},
920
+ {"SyntaxError", "ScriptError"},
921
+ {"ScriptError", "Exception"},
922
+ {"StandardError", "Exception"},
923
+ {"SecurityError", "Exception"},
924
+ {"SignalException", "Exception"},
925
+ {"Interrupt", "SignalException"},
926
+ {"ThreadError", "StandardError"},
927
+ {"ClosedQueueError", "StopIteration"},
928
+ {"NoMatchingPatternError", "StandardError"},
929
+ {"NoMatchingPatternKeyError", "NoMatchingPatternError"},
930
+ {"LocalJumpError", "StandardError"}, /* (#3025) */
931
+ {"SystemExit", "Exception"},
932
+ {"SystemStackError", "Exception"},
933
+ {"NoMemoryError", "Exception"},
934
+ {"SystemCallError", "StandardError"},
935
+ /* The non-blocking readiness exceptions. Each is an Errno subclass that
936
+ also includes IO::WaitReadable / IO::WaitWritable (see
937
+ sp_exc_modules_of_name) -- the module is why a single-parent chain could
938
+ not express them: WaitWritable is included by classes under two
939
+ different Errno parents. */
940
+ {"IO::EAGAINWaitReadable", "Errno::EAGAIN"},
941
+ {"IO::EAGAINWaitWritable", "Errno::EAGAIN"},
942
+ {"IO::EINPROGRESSWaitReadable", "Errno::EINPROGRESS"},
943
+ {"IO::EINPROGRESSWaitWritable", "Errno::EINPROGRESS"},
944
+ {NULL, NULL}
945
+ };
946
+ for (int i = 0; HIER[i][0]; i++)
947
+ if (!strcmp(cls, HIER[i][0])) return HIER[i][1];
948
+ /* Every Errno::* is a SystemCallError, as in CRuby. The names are open --
949
+ sp_file_raise_errno picks one from errno at run time -- so this is a
950
+ prefix rule rather than a table row, and `rescue SystemCallError` catches
951
+ the whole family. */
952
+ if (!strncmp(cls, "Errno::", 7)) return SPL("SystemCallError");
953
+ return NULL;
954
+ }
955
+ /* Does the exception's class have the class-gated accessor `acc` at all?
956
+ The same classes the accessors' own gates admit. A program that adds a
957
+ method of that name to Object reaches it on every other exception, as
958
+ CRuby's lookup does. */
959
+ sp_bool sp_exc_has_acc(sp_Exception *e, const char *acc) {
960
+ if (!e || !acc) return 0;
961
+ const char *c = e->cls_name;
962
+ if (!strcmp(acc, "receiver"))
963
+ return sp_exc_cls_matches(c, "NameError") || sp_exc_cls_matches(c, "KeyError") ||
964
+ sp_exc_cls_matches(c, "FrozenError");
965
+ static const char *const OWN[][2] = {
966
+ {"key", "KeyError"}, {"args", "NoMethodError"}, {"private_call?", "NoMethodError"},
967
+ {"reason", "LocalJumpError"}, {"exit_value", "LocalJumpError"},
968
+ {"tag", "UncaughtThrowError"}, {"value", "UncaughtThrowError"},
969
+ {"status", "SystemExit"}, {"success?", "SystemExit"},
970
+ {"signo", "SignalException"}, {"signm", "SignalException"},
971
+ {"name", "NameError"}, {"errno", "SystemCallError"}, {"result", "StopIteration"},
972
+ };
973
+ for (size_t i = 0; i < sizeof OWN / sizeof OWN[0]; i++)
974
+ if (!strcmp(acc, OWN[i][0])) return sp_exc_cls_matches(c, OWN[i][1]);
975
+ return 0;
976
+ }
977
+ /* NameError#name (NoMethodError inherits it): the carried missing name.
978
+ Any other exception class raises CRuby's NoMethodError -- the receiver
979
+ type is class-erased at compile time, so the check is a runtime one. */
980
+ sp_RbVal sp_exc_name_acc(sp_Exception *e) {SP_GC_ROOT(e);
981
+ if (!e) return sp_box_nil();
982
+ if (sp_exc_cls_matches(e->cls_name, "NameError")) return e->xname;
983
+ sp_raise_cls("NoMethodError",
984
+ sp_sprintf("undefined method 'name' for an instance of %s", e->cls_name));
985
+ }
986
+ /* Exception accessors on a POLY receiver (an exception rescued into a
987
+ union-typed local): unbox and delegate; a non-exception value is CRuby's
988
+ NoMethodError (#3120, #3122). */
989
+ sp_RbVal sp_exc_key_acc(sp_Exception *e) {SP_GC_ROOT(e);
990
+ sp_exc_acc_gate(e, "KeyError", "key");
991
+ /* KeyError.new("m") records no key, and CRuby raises rather than
992
+ answering nil -- nil is a legal key (#3030) */
993
+ if (e && !e->has_key) sp_raise_cls("ArgumentError", "no key is available");
994
+ return e->xkey;
995
+ }
996
+ sp_RbVal sp_exc_receiver_acc(sp_Exception *e) {SP_GC_ROOT(e);
997
+ if (!(e && (sp_exc_cls_matches(e->cls_name, "NameError") ||
998
+ sp_exc_cls_matches(e->cls_name, "KeyError") ||
999
+ sp_exc_cls_matches(e->cls_name, "FrozenError"))))
1000
+ sp_exc_acc_gate(e, "NameError", "receiver");
1001
+ /* an explicitly built NameError.new(msg, name) never recorded one, and
1002
+ CRuby raises rather than answering nil -- nil is a legal receiver (#3036) */
1003
+ if (e && !e->has_recv) sp_raise_cls("ArgumentError", "no receiver is available");
1004
+ return e->xrecv;
1005
+ }
1006
+ /* LoadError#path: no LoadError the program raises carries one (a require is
1007
+ resolved at compile time), so nil -- what one raised by hand answers */
1008
+ sp_RbVal sp_exc_path_acc(sp_Exception *e) {SP_GC_ROOT(e);
1009
+ sp_exc_acc_gate(e, "LoadError", "path");
1010
+ return sp_box_nil();
1011
+ }
1012
+ sp_RbVal sp_exc_args_acc(sp_Exception *e) {SP_GC_ROOT(e);
1013
+ sp_exc_acc_gate(e, "NoMethodError", "args");
1014
+ return e->xkey;
1015
+ }
1016
+ sp_bool sp_exc_private_call_acc(sp_Exception *e) {SP_GC_ROOT(e);
1017
+ sp_exc_acc_gate(e, "NoMethodError", "private_call?");
1018
+ return e ? e->priv_call : 0; /* set only by the explicit .new (#3042) */
1019
+ }
1020
+ sp_RbVal sp_exc_exit_value_acc(sp_Exception *e) {SP_GC_ROOT(e);
1021
+ sp_exc_acc_gate(e, "LocalJumpError", "exit_value");
1022
+ return e->result;
1023
+ }
1024
+ sp_RbVal sp_exc_throw_value_acc(sp_Exception *e) {SP_GC_ROOT(e);
1025
+ sp_exc_acc_gate(e, "UncaughtThrowError", "value");
1026
+ return e->result;
1027
+ }
1028
+ sp_int sp_exc_status_acc(sp_Exception *e) {SP_GC_ROOT(e);
1029
+ sp_exc_acc_gate(e, "SystemExit", "status");
1030
+ return (sp_int)sp_exc_exit_status(e);
1031
+ }
1032
+ sp_bool sp_exc_success_acc(sp_Exception *e) {SP_GC_ROOT(e);
1033
+ sp_exc_acc_gate(e, "SystemExit", "success?");
1034
+ return sp_exc_exit_status(e) == 0;
1035
+ }
1036
+ sp_int sp_exc_signo_acc(sp_Exception *e) {SP_GC_ROOT(e);
1037
+ sp_exc_acc_gate(e, "SignalException", "signo");
1038
+ return (e->xkey.tag == SP_TAG_INT) ? e->xkey.v.i : 0;
1039
+ }
1040
+ const char *sp_exc_signm_acc(sp_Exception *e) {SP_GC_ROOT(e);
1041
+ sp_exc_acc_gate(e, "SignalException", "signm");
1042
+ return sp_exc_message(e);
1043
+ }
1044
+
1045
+ /* `p e` on an exception instance: the same string #inspect answers, for the
1046
+ dispatch a container read or a `p` of a user subclass goes through (#3813). */
1047
+ /* The parts joined by their byte lengths, a NUL in one kept: the message of an exception
1048
+ can hold one (#7556), which a %s would cut. Each part is a Spinel String. The copy is
1049
+ made with no collection, so the parts need no root (as sp_msg_heapify). */
1050
+ const char *sp_exc_cat(int n, ...) {
1051
+ const char *parts[8]; size_t lens[8]; size_t total = 0;
1052
+ va_list ap; va_start(ap, n);
1053
+ for (int i = 0; i < n && i < 8; i++) { parts[i] = va_arg(ap, const char *); lens[i] = sp_str_byte_len(parts[i]); total += lens[i]; }
1054
+ va_end(ap);
1055
+ char *r = sp_str_alloc_nogc(total), *w = r;
1056
+ for (int i = 0; i < n && i < 8; i++) { memcpy(w, parts[i], lens[i]); w += lens[i]; }
1057
+ return r;
1058
+ }
1059
+ /* "<Class>: <msg>" and "<msg> (<Class>)": the class name is read from the exception
1060
+ itself (no allocation), msg is the Spinel String the caller computed first, and the
1061
+ copy runs with no collection, so no argument needs a root. */
1062
+ const char *sp_exc_full_text(volatile sp_Exception *ve, const char *msg) {
1063
+ sp_Exception *e = (sp_Exception *)ve;
1064
+ const char *cn = e && e->cls_name ? e->cls_name : "RuntimeError";
1065
+ size_t lc = strlen(cn), lm = sp_str_byte_len(msg);
1066
+ char *r = sp_str_alloc_nogc(lc + 2 + lm);
1067
+ memcpy(r, cn, lc); memcpy(r + lc, ": ", 2); memcpy(r + lc + 2, msg, lm);
1068
+ return r;
1069
+ }
1070
+ const char *sp_exc_detailed_text(volatile sp_Exception *ve, const char *msg) {
1071
+ sp_Exception *e = (sp_Exception *)ve;
1072
+ const char *cn = e && e->cls_name ? e->cls_name : "RuntimeError";
1073
+ size_t lc = strlen(cn), lm = sp_str_byte_len(msg);
1074
+ char *r = sp_str_alloc_nogc(lm + 2 + lc + 1);
1075
+ memcpy(r, msg, lm); memcpy(r + lm, " (", 2); memcpy(r + lm + 2, cn, lc); r[lm + 2 + lc] = ')';
1076
+ return r;
1077
+ }
1078
+ const char *sp_exc_inspect(void *p) {
1079
+ sp_Exception *e = (sp_Exception *)p;
1080
+ if (!e) return SPL("nil");
1081
+ SP_GC_ROOT(e);
1082
+ const char *msg = sp_exc_to_s_text(e);
1083
+ SP_GC_ROOT(msg);
1084
+ const char *cn = sp_exc_class_name(e);
1085
+ SP_GC_ROOT(cn);
1086
+ return (!msg || !sp_str_byte_len(msg)) ? cn : sp_exc_cat(5, SPL("#<"), cn, SPL(": "), msg, SPL(">"));
1087
+ }
1088
+
1089
+ const char *(*sp_user_exc_to_s_fn)(sp_Exception *) = NULL;
1090
+ const char *sp_exc_to_s_text(sp_Exception *e) {
1091
+ return sp_user_exc_to_s_fn ? sp_user_exc_to_s_fn(e) : sp_exc_message(e);
1092
+ }
1093
+
1094
+ /* CRuby's arity ArgumentError (sp_exc.h): the message is built once here for
1095
+ every count only the run time knows, as arity_message builds it for the
1096
+ counts the compiler sees. */
1097
+ SP_NORETURN void sp_raise_arity(sp_int given, sp_int min, sp_int max, const char *kw) {
1098
+ const char *msg;
1099
+ if (!kw) kw = "";
1100
+ if (min == max)
1101
+ msg = sp_sprintf("wrong number of arguments (given %lld, expected %lld%s)",
1102
+ (long long)given, (long long)min, kw);
1103
+ else if (max < 0)
1104
+ msg = sp_sprintf("wrong number of arguments (given %lld, expected %lld+%s)",
1105
+ (long long)given, (long long)min, kw);
1106
+ else
1107
+ msg = sp_sprintf("wrong number of arguments (given %lld, expected %lld..%lld%s)",
1108
+ (long long)given, (long long)min, (long long)max, kw);
1109
+ /* the message lives on the collected heap and the raise allocates */
1110
+ SP_GC_ROOT_STR(msg);
1111
+ sp_raise_cls("ArgumentError", msg);
1112
+ }
1113
+ /* The keyword ArgumentErrors (sp_exc.h), as kw_error_message spells them
1114
+ for the lists the compiler sees. */
1115
+ SP_NORETURN void sp_raise_kw_error(const char *kind, sp_int count, const char *names) {
1116
+ const char *msg = sp_sprintf("%s keyword%s: %s", kind, count > 1 ? "s" : "", names);
1117
+ SP_GC_ROOT_STR(msg);
1118
+ sp_raise_cls("ArgumentError", msg);
1119
+ }
1120
+
1121
+ /* Exception#is_a?(ClassName): checks class name and known hierarchy. */
1122
+ sp_int sp_exc_is_a(volatile sp_Exception *ve, const char *cn) {
1123
+ sp_Exception *e = (sp_Exception *)ve;
1124
+ if (!e || !cn) return 0;
1125
+ /* one authority for "does this level answer to cn", modules included: the
1126
+ matcher rescue arms use. Without it #is_a?(SomeModule) said false where
1127
+ `rescue SomeModule` said yes (#3366 follow-up). */
1128
+ cn = sp_exc_canonical_name(cn);
1129
+ if (sp_exc_cls_matches(e->cls_name, cn)) return 1;
1130
+ /* find the exception's class chain and check if cn appears in it */
1131
+ const char *cls = e->cls_name;
1132
+ int used_parent = 0;
1133
+ for (int depth = 0; depth < 20 && cls; depth++) {
1134
+ if (!strcmp(cls, cn)) return 1;
1135
+ const char *parent = sp_exc_parent_of_name(cls);
1136
+ if (!parent) {
1137
+ /* unknown (user) class: try user hierarchy first */
1138
+ if (sp_user_exc_parent_fn) { parent = sp_user_exc_parent_fn(cls); }
1139
+ if (!parent) {
1140
+ if (!used_parent && e->parent_cls_name) {
1141
+ cls = e->parent_cls_name;
1142
+ used_parent = 1;
1143
+ continue;
1144
+ }
1145
+ if (!strcmp(cn, "Exception")) return 1;
1146
+ if (!strcmp(cn, "Object") || !strcmp(cn, "BasicObject")) return 1;
1147
+ break;
1148
+ }
1149
+ }
1150
+ cls = parent;
1151
+ }
1152
+ if (!strcmp(cn, "Object") || !strcmp(cn, "BasicObject") || !strcmp(cn, "Kernel")) return 1;
1153
+ return 0;
1154
+ }
1155
+
1156
+ /* Each of the fixed-depth handler stacks in spinel_rt.h fails the same way when
1157
+ a program nests deeper than its array holds; see the comment there. */
1158
+ SP_NORETURN SP_COLD void sp_stack_too_deep(void) {
1159
+ fputs("stack level too deep (SystemStackError)\n", stderr);
1160
+ exit(1);
1161
+ }
1162
+
1163
+ /* The per-fiber handler context (lib/sp_exc_ctx.h): the operations that touch
1164
+ only the context itself. */
1165
+ void *sp_exc_ctx_new(void) { return calloc(1, sizeof(sp_exc_ctx_t)); }
1166
+ void sp_exc_ctx_free(void *p) {
1167
+ sp_exc_ctx_t *x = (sp_exc_ctx_t *)p;
1168
+ if (!x) return;
1169
+ free(x->es); free(x->em); free(x->ec); free(x->eo);
1170
+ free(x->cs); free(x->ct); free(x->ctk); free(x->cv); free(x->cet);
1171
+ free(x->bs); free(x->bv); free(x->bser); free(x->bet); free(x->shand);
1172
+ free(x->rrf); free(x->rrem); free(x->rrcm); free(x->rrbm);
1173
+ free(x->erm); free(x->ersm); free(x->crm); free(x);
1174
+ }
1175
+ void sp_exc_ctx_mark(void *p) { /* GC: mark a suspended fiber's carried exc objects */
1176
+ sp_exc_ctx_t *x = (sp_exc_ctx_t *)p;
1177
+ if (!x) return;
1178
+ for (int i = 0; i < x->en; i++) if (x->eo[i]) sp_gc_mark(x->eo[i]);
1179
+ /* a suspended fiber's proc-return chain (nodes on its preserved C stack) may
1180
+ carry an in-flight return value; mark each so it survives a GC during yield. */
1181
+ for (sp_proc_home *h = x->prhead; h; h = h->prev) sp_mark_rbval(h->val);
1182
+ for (int i = 0; i < x->bn; i++) sp_mark_rbval(x->bv[i]); /* carried break scopes */
1183
+ for (int i = 0; i < x->rn; i++) if (x->shand[i]) sp_gc_mark(x->shand[i]); /* handled excs */
1184
+ if (x->icause) sp_gc_mark(x->icause); /* the one a suspended ensure body runs for */
1185
+ }