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.
- checksums.yaml +7 -0
- data/LICENSE-mruby.txt +22 -0
- data/LICENSE-spinel.txt +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/ext/qr_reader_native/SPINEL_VERSION +1 -0
- data/ext/qr_reader_native/extconf.rb +8 -0
- data/ext/qr_reader_native/mruby/error.h +13 -0
- data/ext/qr_reader_native/mruby/internal.h +145 -0
- data/ext/qr_reader_native/mruby/string.h +22 -0
- data/ext/qr_reader_native/mruby.h +93 -0
- data/ext/qr_reader_native/mruby_shim.h +118 -0
- data/ext/qr_reader_native/qr_reader_native.c +13513 -0
- data/ext/qr_reader_native/qr_reader_native.h +29 -0
- data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
- data/ext/qr_reader_native/re_cased.h +71 -0
- data/ext/qr_reader_native/re_compile.c +5784 -0
- data/ext/qr_reader_native/re_ctype.h +3540 -0
- data/ext/qr_reader_native/re_exec.c +2190 -0
- data/ext/qr_reader_native/re_internal.h +671 -0
- data/ext/qr_reader_native/re_prop.h +4629 -0
- data/ext/qr_reader_native/re_spinel.c +363 -0
- data/ext/qr_reader_native/re_spinel.h +22 -0
- data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
- data/ext/qr_reader_native/re_utf8.c +156 -0
- data/ext/qr_reader_native/sp_alloc.c +1447 -0
- data/ext/qr_reader_native/sp_alloc.h +1196 -0
- data/ext/qr_reader_native/sp_argf.h +33 -0
- data/ext/qr_reader_native/sp_array.c +1052 -0
- data/ext/qr_reader_native/sp_array.h +456 -0
- data/ext/qr_reader_native/sp_bigint.c +5826 -0
- data/ext/qr_reader_native/sp_bigint.h +117 -0
- data/ext/qr_reader_native/sp_cold.c +5259 -0
- data/ext/qr_reader_native/sp_compat.h +402 -0
- data/ext/qr_reader_native/sp_core.c +818 -0
- data/ext/qr_reader_native/sp_core.h +56 -0
- data/ext/qr_reader_native/sp_crypto.c +877 -0
- data/ext/qr_reader_native/sp_crypto.h +169 -0
- data/ext/qr_reader_native/sp_dtoa.c +1533 -0
- data/ext/qr_reader_native/sp_dtoa.h +29 -0
- data/ext/qr_reader_native/sp_encoding_names.h +172 -0
- data/ext/qr_reader_native/sp_enum.h +86 -0
- data/ext/qr_reader_native/sp_exc.c +1185 -0
- data/ext/qr_reader_native/sp_exc.h +186 -0
- data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
- data/ext/qr_reader_native/sp_fiber.c +889 -0
- data/ext/qr_reader_native/sp_fiber.h +162 -0
- data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
- data/ext/qr_reader_native/sp_format.c +695 -0
- data/ext/qr_reader_native/sp_format.h +83 -0
- data/ext/qr_reader_native/sp_gc.c +1793 -0
- data/ext/qr_reader_native/sp_gc.h +762 -0
- data/ext/qr_reader_native/sp_hash.c +131 -0
- data/ext/qr_reader_native/sp_hash.h +131 -0
- data/ext/qr_reader_native/sp_inspect.c +215 -0
- data/ext/qr_reader_native/sp_inspect.h +128 -0
- data/ext/qr_reader_native/sp_io.c +1676 -0
- data/ext/qr_reader_native/sp_io.h +272 -0
- data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
- data/ext/qr_reader_native/sp_iobuffer.h +167 -0
- data/ext/qr_reader_native/sp_marshal.c +487 -0
- data/ext/qr_reader_native/sp_marshal.h +60 -0
- data/ext/qr_reader_native/sp_net.c +1029 -0
- data/ext/qr_reader_native/sp_net.h +205 -0
- data/ext/qr_reader_native/sp_pack.c +1254 -0
- data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
- data/ext/qr_reader_native/sp_proc.c +410 -0
- data/ext/qr_reader_native/sp_proc.h +327 -0
- data/ext/qr_reader_native/sp_process.c +547 -0
- data/ext/qr_reader_native/sp_process_status.c +211 -0
- data/ext/qr_reader_native/sp_process_status.h +66 -0
- data/ext/qr_reader_native/sp_random.c +287 -0
- data/ext/qr_reader_native/sp_random.h +52 -0
- data/ext/qr_reader_native/sp_range.h +144 -0
- data/ext/qr_reader_native/sp_re.c +1379 -0
- data/ext/qr_reader_native/sp_re.h +186 -0
- data/ext/qr_reader_native/sp_re_names.h +59 -0
- data/ext/qr_reader_native/sp_sched.c +4044 -0
- data/ext/qr_reader_native/sp_sched.h +295 -0
- data/ext/qr_reader_native/sp_slab.c +1507 -0
- data/ext/qr_reader_native/sp_str.c +1462 -0
- data/ext/qr_reader_native/sp_str.h +306 -0
- data/ext/qr_reader_native/sp_str_crypt.c +23 -0
- data/ext/qr_reader_native/sp_string.c +92 -0
- data/ext/qr_reader_native/sp_string.h +253 -0
- data/ext/qr_reader_native/sp_system.c +78 -0
- data/ext/qr_reader_native/sp_system.h +18 -0
- data/ext/qr_reader_native/sp_time.c +860 -0
- data/ext/qr_reader_native/sp_time.h +100 -0
- data/ext/qr_reader_native/sp_types.h +292 -0
- data/ext/qr_reader_native/spinel_rt.h +19017 -0
- data/ext/qr_reader_native/unicase.c +399 -0
- data/ext/qr_reader_native/unicase.h +901 -0
- data/lib/qr_reader/native.rb +6 -0
- 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
|
+
}
|