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,327 @@
|
|
|
1
|
+
#ifndef SP_PROC_H
|
|
2
|
+
#define SP_PROC_H
|
|
3
|
+
/* sp_proc.h -- sp_Proc / sp_Curry struct layouts + cold ops.
|
|
4
|
+
*
|
|
5
|
+
* sp_proc_call itself (the hot per-call dispatch) stays a plain
|
|
6
|
+
* (non-static) function defined directly in spinel_rt.h -- like
|
|
7
|
+
* sp_sprintf/sp_argv, its body is resolved at the final link against the
|
|
8
|
+
* generated TU, so it doesn't need to move for lib/sp_proc.c to reach it.
|
|
9
|
+
* _sp_proc_poly_args/_sp_proc_poly_ret (the boxed calling-convention side
|
|
10
|
+
* channel) are likewise already non-static SP_TLS globals in
|
|
11
|
+
* spinel_rt.h; only extern declarations are needed here.
|
|
12
|
+
*
|
|
13
|
+
* Proc#>>/<</compose and the Proc-return (non-local return via longjmp)
|
|
14
|
+
* machinery stay in spinel_rt.h: sp_proc_compose_fn calls sp_poly_to_i
|
|
15
|
+
* (a value-dispatch function that's hot in optcarrot and excluded from
|
|
16
|
+
* eviction), and proc-return threads through the TU-local exception-stack
|
|
17
|
+
* globals (sp_exc_top / sp_unwind_kind / sp_proc_ret_head).
|
|
18
|
+
*
|
|
19
|
+
* 0 optcarrot uses for every function below.
|
|
20
|
+
*/
|
|
21
|
+
#include "sp_types.h" /* sp_int, sp_sym, sp_bool */
|
|
22
|
+
#include "sp_gc.h" /* sp_RbVal, sp_gc_alloc, sp_gc_mark */
|
|
23
|
+
#include "sp_alloc.h" /* sp_PolyArray, sp_box_sym, sp_box_poly_array, sp_raise_cls */
|
|
24
|
+
|
|
25
|
+
typedef struct sp_Proc { void *fn; void *cap; void (*cap_scan)(void *); sp_int arity; sp_bool lambda_p; sp_int param_count; const sp_sym *param_kinds; const sp_sym *param_names; sp_bool frozen; /* Object#freeze observed (sp_gc_alloc zero-fills) */ void *origin; /* dup/clone lineage root for Proc#== (NULL: self is the root) */ sp_int ie_cls; } sp_Proc;
|
|
26
|
+
/* arity: the count this accumulator realizes at -- Proc#curry(n)'s n, else
|
|
27
|
+
the target's required-parameter count (CRuby's min arity, so a variadic
|
|
28
|
+
base realizes on its first call). Carried here so a curry that travels
|
|
29
|
+
through a container or an untyped slot still knows when it is done. */
|
|
30
|
+
typedef struct { sp_Proc *target; sp_int arity; sp_int nargs; sp_RbVal args[16]; } sp_Curry;
|
|
31
|
+
|
|
32
|
+
sp_int sp_proc_call(sp_Proc *p, sp_int argc, sp_int *args); /* defined in the generated TU */
|
|
33
|
+
/* The proc calling convention's boxed side channel: how many arguments it
|
|
34
|
+
carries. Every publisher, the GC scan that keeps them alive, and the gates
|
|
35
|
+
that decline a longer call read this one name -- a second copy of the
|
|
36
|
+
number in any of them is a call whose arguments are published and never
|
|
37
|
+
marked, or marked and never passed. Machine-generated code reaches 17
|
|
38
|
+
parameters and the old 16 refused it outright. */
|
|
39
|
+
#define SP_PROC_ARG_SLOTS 64
|
|
40
|
+
extern SP_TLS sp_RbVal _sp_proc_poly_args[SP_PROC_ARG_SLOTS]; /* defined in the generated TU */
|
|
41
|
+
extern SP_TLS sp_RbVal _sp_proc_poly_ret; /* defined in the generated TU */
|
|
42
|
+
|
|
43
|
+
/* The lineage root of a proc: dups/clones of one proc share it, so Proc#== /
|
|
44
|
+
#eql? compare roots (a dup == its original) while distinct literals differ. */
|
|
45
|
+
static inline sp_Proc *sp_proc_root(sp_Proc *p) { return (p && p->origin) ? (sp_Proc *)p->origin : p; }
|
|
46
|
+
|
|
47
|
+
void sp_Proc_scan(void *p);
|
|
48
|
+
sp_Proc *sp_proc_new_meta(void *fn, void *cap, void (*cap_scan)(void *), sp_int arity, sp_bool lambda_p, sp_int param_count, const sp_sym *param_kinds, const sp_sym *param_names);
|
|
49
|
+
sp_Proc *sp_proc_dup(sp_Proc *p, int keep_frozen);
|
|
50
|
+
sp_Proc *sp_proc_new(void *fn, void *cap, void (*cap_scan)(void *));
|
|
51
|
+
sp_int sp_proc_arity(sp_Proc *p);
|
|
52
|
+
sp_bool sp_proc_lambda_p(sp_Proc *p);
|
|
53
|
+
const char *sp_proc_inspect(sp_Proc *p);
|
|
54
|
+
sp_PolyArray *sp_proc_parameters_ids(sp_Proc *p, int mode, sp_sym req_id, sp_sym opt_id);
|
|
55
|
+
sp_PolyArray *sp_proc_parameters(sp_Proc *p);
|
|
56
|
+
void sp_curry_scan(void *p);
|
|
57
|
+
sp_Curry *sp_curry_new(sp_Proc *p);
|
|
58
|
+
sp_Curry *sp_curry_new_n(sp_Proc *p, sp_int n, sp_int max);
|
|
59
|
+
sp_Curry *sp_curry_apply(sp_Curry *c, sp_RbVal arg);
|
|
60
|
+
void sp_curry_publish_args(sp_Curry *c);
|
|
61
|
+
|
|
62
|
+
/* ---- BoundMethod (Method object): sp_bound_method_new is hot in
|
|
63
|
+
optcarrot (69 uses), so it stays static inline here -- pure textual
|
|
64
|
+
move, same per-TU inlining as before. sp_BoundMethod_scan is a GC
|
|
65
|
+
callback (only ever invoked indirectly through the function pointer
|
|
66
|
+
sp_bound_method_new hands to sp_gc_alloc), so moving its body to
|
|
67
|
+
lib/sp_proc.c costs nothing -- taking its address from an inline
|
|
68
|
+
context is just an embedded constant, not a call site. ---- */
|
|
69
|
+
/* What `self` holds, so the scan knows whether to follow it: a Method can be
|
|
70
|
+
bound to an Integer or a Float as easily as to an object, and marking the
|
|
71
|
+
raw value as a pointer reads whatever address it names. */
|
|
72
|
+
#define SP_BM_SELF_NONE 0 /* not a reference: a number, a class value, unbound */
|
|
73
|
+
#define SP_BM_SELF_OBJ 1
|
|
74
|
+
#define SP_BM_SELF_STR 2
|
|
75
|
+
/* How the legacy sp_int C return is boxed back into a Ruby value. Any method
|
|
76
|
+
whose C return fits the register records its kind here: a plain Integer, a
|
|
77
|
+
`const char *` String, a nullable `sp_Bigint *`, a void/nil, a bool, a
|
|
78
|
+
Symbol id, a typed-array or user-object pointer. A synthesized typed-array
|
|
79
|
+
adapter (`<int_array>.method(:push)`) launders the array/string it returns
|
|
80
|
+
through the same register. */
|
|
81
|
+
#define SP_BM_RET_INT 0
|
|
82
|
+
#define SP_BM_RET_STR 1
|
|
83
|
+
#define SP_BM_RET_INT_ARRAY 2
|
|
84
|
+
#define SP_BM_RET_STR_ARRAY 3
|
|
85
|
+
/* A void C return (a Ruby method whose value is nil, or unused): the cast
|
|
86
|
+
reads an undefined register and the answer is nil regardless. */
|
|
87
|
+
#define SP_BM_RET_NIL 4
|
|
88
|
+
/* The C function returns sp_RbVal (a poly Ruby return): a 16-byte struct
|
|
89
|
+
comes back in two registers, which no sp_int cast can read, so the call
|
|
90
|
+
arms take the sp_RbVal cast for this kind and box nothing. */
|
|
91
|
+
#define SP_BM_RET_POLY 5
|
|
92
|
+
/* A bool C return: the callee writes the low byte of the return register and
|
|
93
|
+
leaves the rest undefined (SysV x86-64 and AArch64 both), so the low byte
|
|
94
|
+
is the whole answer. A Symbol is its id in an sp_int. */
|
|
95
|
+
#define SP_BM_RET_BOOL 6
|
|
96
|
+
#define SP_BM_RET_SYM 7
|
|
97
|
+
/* A user-class pointer return: the low byte is the kind and the bits above it
|
|
98
|
+
carry the class id the bind site knew statically (a class without
|
|
99
|
+
subclasses), or nothing when the object carries its own id in its first
|
|
100
|
+
field (a subclassed class: the _dyn box reads it, as the generated boxing
|
|
101
|
+
sites do). Unlike the kinds above, this one is NOT a plain enumerator, so
|
|
102
|
+
sp_bm_box_ret matches on the low byte. */
|
|
103
|
+
#define SP_BM_RET_OBJ 8
|
|
104
|
+
#define SP_BM_RET_OBJ_DYN 9
|
|
105
|
+
/* A Bigint return: a nullable `sp_Bigint *` riding the register, boxed into
|
|
106
|
+
the Ruby value it points at (NULL boxes as nil). */
|
|
107
|
+
#define SP_BM_RET_BIGINT 10
|
|
108
|
+
#define SP_BM_RET_KIND(r) ((r) & 0xff)
|
|
109
|
+
#define SP_BM_RET_OBJ_OF(cls) (SP_BM_RET_OBJ | ((sp_int)(cls) << 8))
|
|
110
|
+
typedef struct sp_BoundMethod { void *self; sp_int fn; const char *name; sp_int arity;
|
|
111
|
+
const char *desc; /* compile-time #inspect rendering ("#<Method: Owner#name(params)>"), or NULL */
|
|
112
|
+
sp_int self_kind; /* SP_BM_SELF_* */
|
|
113
|
+
sp_int unbound; /* built by #unbind on a boxed Method: reports UnboundMethod */
|
|
114
|
+
sp_int recv_bound; /* the target's C ABI takes the bound receiver as its leading
|
|
115
|
+
argument, so the self-ful cast must be used even when
|
|
116
|
+
`self` is NULL because the receiver VALUE is zero
|
|
117
|
+
(Integer 0, false, nil) (#4395). `self != NULL` cannot
|
|
118
|
+
tell a top-level method (self-less) from a
|
|
119
|
+
receiver-bound wrapper whose value is 0 (#4395); this
|
|
120
|
+
flag is the bind site's receiver-bound fact. It is
|
|
121
|
+
independent of legacy_int_abi: an object-bound Method
|
|
122
|
+
with a non-int return still needs its self slot. */
|
|
123
|
+
sp_int legacy_int_abi; /* whether the target can ride the legacy sp_int-cast poly-call
|
|
124
|
+
path: 0 declines, 1 callable. A regular method rides when
|
|
125
|
+
its C return is a box-able kind recorded by legacy_ret
|
|
126
|
+
(Integer/String/Bigint/nil/bool/Symbol/typed array/user
|
|
127
|
+
object); a float, by-value struct, or unclassifiable
|
|
128
|
+
return declines. A synthesized __bam_ wrapper and the
|
|
129
|
+
typed-array adapters record their kind the same way. See
|
|
130
|
+
method_legacy_int_abi (#4395). */
|
|
131
|
+
const char *legacy_sig; /* per-position C ABI type tokens, eight chars each, or NULL.
|
|
132
|
+
Each scalar kind (int/bool/symbol/nil) has its own token,
|
|
133
|
+
TY_UNKNOWN is the wildcard 0, and every other kind uses
|
|
134
|
+
100000 + TyKind (an object class encodes its class id in
|
|
135
|
+
TyKind), so a bool parameter never accepts an int and a
|
|
136
|
+
String parameter never accepts an IntArray. */
|
|
137
|
+
sp_int legacy_fixed; /* number of fixed positional C slots the signature fills */
|
|
138
|
+
sp_int legacy_rest; /* always 0 now: method_legacy_int_abi declines every rest
|
|
139
|
+
parameter (its trailing sp_PolyArray* has no slot in the
|
|
140
|
+
static cast), but the runtime gate keeps the rest-form
|
|
141
|
+
check for any future caller that stamps it */
|
|
142
|
+
sp_int legacy_ret; /* SP_BM_RET_*: how to box the sp_int C return */
|
|
143
|
+
sp_int poly_abi; /* whether the target's C signature is the promote poly
|
|
144
|
+
ABI -- `sp_RbVal fn([void *self,] sp_RbVal...)`:
|
|
145
|
+
every fixed parameter slot and the return are
|
|
146
|
+
TY_POLY, with the same structural declines as the
|
|
147
|
+
legacy classifier (rest/keyword/post-rest, an
|
|
148
|
+
explicit &blk, a leading _sp_cls). Stamped at bind
|
|
149
|
+
time by method_poly_abi (codegen_call.c); the
|
|
150
|
+
promote poly-call arms gate on it instead of
|
|
151
|
+
assuming every target is poly-signatured -- a
|
|
152
|
+
Float-parameter or String-returning target read the
|
|
153
|
+
sp_RbVal registers as garbage (and wasm, whose
|
|
154
|
+
indirect calls check the callee signature, traps). */
|
|
155
|
+
sp_int poly_fixed; /* number of fixed sp_RbVal slots the poly signature
|
|
156
|
+
reads; a call must pass exactly this count */
|
|
157
|
+
sp_int thunk; /* the per-target THUNK the bind site synthesized (#4542):
|
|
158
|
+
`sp_int fn(void *cap, sp_int argc, sp_int *args)`, the
|
|
159
|
+
proc-ABI shape of sp_method_proc_tramp itself, reading
|
|
160
|
+
every argument from the boxed side-channel, converting
|
|
161
|
+
each to the parameter's C type (TypeError otherwise),
|
|
162
|
+
filling omitted optionals from their defaults, packing
|
|
163
|
+
a rest, and publishing the boxed result. It covers the
|
|
164
|
+
signatures neither ABI stamp accepts (a Float, a
|
|
165
|
+
default below full arity, a rest, a mixed promote
|
|
166
|
+
signature); 0 when the bind site had no static target
|
|
167
|
+
or the target's shape declines (keywords). */
|
|
168
|
+
sp_int thunk_min, thunk_max; /* the argument counts it binds (max 16 for a rest) */
|
|
169
|
+
} sp_BoundMethod;
|
|
170
|
+
void sp_bm_cap_scan(void *p);
|
|
171
|
+
sp_int sp_method_proc_tramp(void *cap, sp_int argc, sp_int *args);
|
|
172
|
+
sp_Proc *sp_method_to_proc(sp_BoundMethod *m);
|
|
173
|
+
void sp_BoundMethod_scan(void *p);
|
|
174
|
+
|
|
175
|
+
/* The target's address as the Method stores it. On wasm the C compiler
|
|
176
|
+
turns a direct call through a function pointer of another type into a
|
|
177
|
+
thunk, and where it cannot convert an argument or the return (a bool
|
|
178
|
+
against a pointer or an sp_int) the thunk is a trap; it sees such a call
|
|
179
|
+
when it can prove which function the slot holds, which after inlining
|
|
180
|
+
the constructor it can. The slot goes through memory the optimiser may
|
|
181
|
+
not look through, and every call is the indirect one the ABI means, whose
|
|
182
|
+
check is the wasm signature (all i32) and passes. Elsewhere the store is
|
|
183
|
+
the store. */
|
|
184
|
+
static inline sp_int sp_bm_fn_opaque(sp_int fn) {
|
|
185
|
+
#if defined(__wasm__)
|
|
186
|
+
volatile sp_int v = fn;
|
|
187
|
+
return v;
|
|
188
|
+
#else
|
|
189
|
+
return fn;
|
|
190
|
+
#endif
|
|
191
|
+
}
|
|
192
|
+
static inline sp_BoundMethod *sp_bound_method_new(void *self, sp_int self_kind, sp_int fn, const char *name, sp_int arity) { sp_BoundMethod *m = (sp_BoundMethod *)sp_gc_alloc(sizeof(sp_BoundMethod), NULL, sp_BoundMethod_scan); m->self = self; m->self_kind = self_kind; m->fn = sp_bm_fn_opaque(fn); m->name = name; m->arity = arity; m->desc = NULL; m->unbound = 0; m->recv_bound = 0; m->legacy_int_abi = 0; m->legacy_sig = NULL; m->legacy_fixed = 0; m->legacy_rest = 0; m->legacy_ret = SP_BM_RET_INT; m->poly_abi = 0; m->poly_fixed = 0; m->thunk = 0; m->thunk_min = 0; m->thunk_max = 0; return m; }
|
|
193
|
+
/* Tag a freshly-built Method with whether its target has the legacy sp_int C
|
|
194
|
+
ABI, the per-position type signature, the fixed/rest slot counts, and how
|
|
195
|
+
its sp_int C return boxes. The constructors default to 0 (unsafe), so every
|
|
196
|
+
statically-known binding sets this before the Method can reach a poly slot
|
|
197
|
+
(#4395). */
|
|
198
|
+
static inline sp_BoundMethod *sp_bm_set_abi(sp_BoundMethod *m, sp_int recv_bound, sp_int legacy_int_abi, const char *legacy_sig, sp_int legacy_fixed, sp_int legacy_rest, sp_int legacy_ret, sp_int poly_abi, sp_int poly_fixed) { m->recv_bound = recv_bound; m->legacy_int_abi = legacy_int_abi; m->legacy_sig = legacy_sig; m->legacy_fixed = legacy_fixed; m->legacy_rest = legacy_rest; m->legacy_ret = legacy_ret; m->poly_abi = poly_abi; m->poly_fixed = poly_fixed; return m; }
|
|
199
|
+
/* Stamp the bind site's thunk (0 for none) and the counts it binds. */
|
|
200
|
+
static inline sp_BoundMethod *sp_bm_set_thunk(sp_BoundMethod *m, sp_int thunk, sp_int tmin, sp_int tmax) { m->thunk = sp_bm_fn_opaque(thunk); m->thunk_min = tmin; m->thunk_max = tmax; return m; }
|
|
201
|
+
/* Whether a call passing `argc` arguments takes the thunk: a bound Method
|
|
202
|
+
with one, the count within the side-channel's slots. A count the
|
|
203
|
+
signature cannot bind is the thunk's own ArgumentError, in CRuby's words,
|
|
204
|
+
so the range is not tested here. */
|
|
205
|
+
static inline sp_bool sp_bm_thunk_ok(sp_BoundMethod *m, sp_int argc) {
|
|
206
|
+
return m && !m->unbound && m->thunk && argc <= SP_PROC_ARG_SLOTS;
|
|
207
|
+
}
|
|
208
|
+
/* Box the raw sp_int a legacy-ABI Method returned according to the Ruby return
|
|
209
|
+
the bind site recorded. A plain Integer return is SP_BM_RET_INT; a String or
|
|
210
|
+
Bigint return, an array-returning method, and a void/bool/Symbol return each
|
|
211
|
+
box their own kind; a typed array adapter that returns self (push) or a
|
|
212
|
+
laundered element (StrArray get/set) boxes the real value instead of
|
|
213
|
+
mis-tagging the pointer as an Integer (#4395). A Bigint pointer boxes into
|
|
214
|
+
its Ruby value. SP_BM_RET_INT
|
|
215
|
+
goes through sp_box_int_or_nil: an IntArray
|
|
216
|
+
`[]` out of range answers the nullable SP_INT_NIL sentinel (INTPTR_MIN),
|
|
217
|
+
which sp_box_int would hand back as a truthy Integer instead of nil, and a
|
|
218
|
+
regular TY_INT method uses the same reserved sentinel for nil (see
|
|
219
|
+
sp_poly_as_int_or_nil / sp_box_int_or_nil in sp_alloc.h) so boxing it as nil
|
|
220
|
+
is the documented invariant. */
|
|
221
|
+
static inline sp_RbVal sp_bm_box_ret(sp_BoundMethod *m, sp_int raw) {
|
|
222
|
+
sp_int r = m ? m->legacy_ret : SP_BM_RET_INT;
|
|
223
|
+
switch (SP_BM_RET_KIND(r)) {
|
|
224
|
+
case SP_BM_RET_STR: return sp_box_str((const char *)(uintptr_t)raw);
|
|
225
|
+
case SP_BM_RET_INT_ARRAY: return sp_box_nullable_obj((void *)(uintptr_t)raw, SP_BUILTIN_INT_ARRAY);
|
|
226
|
+
case SP_BM_RET_STR_ARRAY: return sp_box_nullable_obj((void *)(uintptr_t)raw, SP_BUILTIN_STR_ARRAY);
|
|
227
|
+
case SP_BM_RET_NIL: return sp_box_nil();
|
|
228
|
+
case SP_BM_RET_BOOL: return sp_box_bool((raw & 0xff) != 0);
|
|
229
|
+
case SP_BM_RET_SYM: return (sp_sym)raw != (sp_sym)-1 ? sp_box_sym((sp_sym)raw) : sp_box_nil();
|
|
230
|
+
case SP_BM_RET_OBJ: return sp_box_nullable_obj((void *)(uintptr_t)raw, (int)(r >> 8));
|
|
231
|
+
case SP_BM_RET_OBJ_DYN: return sp_box_nullable_obj_dyn((void *)(uintptr_t)raw, 0);
|
|
232
|
+
case SP_BM_RET_BIGINT: return raw ? sp_box_bigint((sp_Bigint *)(uintptr_t)raw) : sp_box_nil();
|
|
233
|
+
default: return sp_box_int_or_nil(raw);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/* The raw register as the trampoline hands it back untouched to a typed
|
|
237
|
+
caller: a bool callee wrote only the low byte, so normalise it to 0/1 there;
|
|
238
|
+
every other kind is already the value (an int, a Symbol id, a pointer). */
|
|
239
|
+
static inline sp_int sp_bm_norm_ret(sp_BoundMethod *m, sp_int raw) {
|
|
240
|
+
return (m && SP_BM_RET_KIND(m->legacy_ret) == SP_BM_RET_BOOL) ? ((raw & 0xff) != 0) : raw;
|
|
241
|
+
}
|
|
242
|
+
/* Mark a statically-built instance_method/#unbind result as an UnboundMethod,
|
|
243
|
+
so a later dynamic .call/[] through a container sees m->unbound and raises
|
|
244
|
+
instead of invoking the instance C function with no self (#4395). */
|
|
245
|
+
static inline sp_BoundMethod *sp_bm_set_unbound(sp_BoundMethod *m) { if (m) m->unbound = 1; return m; }
|
|
246
|
+
/* The scalar-kind ABI token of a boxed argument (see abi_sig_token in
|
|
247
|
+
codegen_call.c: TY_INT/TY_BOOL/TY_SYMBOL/TY_NIL are 1/2/3/4), or 0 when the
|
|
248
|
+
value has no scalar sp_int slot at all (a pointer, Float, or Bigint). The
|
|
249
|
+
generic Method trampoline and the poly spread path read the same encoding as
|
|
250
|
+
the statically-typed sp_bm_legacy_abi_ok. */
|
|
251
|
+
static inline sp_int sp_bm_boxed_scalar_token(sp_RbVal e) {
|
|
252
|
+
switch (e.tag) {
|
|
253
|
+
case SP_TAG_INT: return 1; /* TY_INT */
|
|
254
|
+
case SP_TAG_BOOL: return 2; /* TY_BOOL */
|
|
255
|
+
case SP_TAG_SYM: return 3; /* TY_SYMBOL */
|
|
256
|
+
case SP_TAG_NIL: return 4; /* TY_NIL */
|
|
257
|
+
default: return 0; /* pointer/Float/Bigint: no scalar slot */
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/* Whether a boxed argument with scalar token `code` fits the signature's slot
|
|
261
|
+
at position `i`, under the same rule as sp_bm_sig_pos_match: an exact
|
|
262
|
+
scalar-kind match, or the TY_UNKNOWN 0 wildcard on the parameter side. A
|
|
263
|
+
code of 0 (a value with no scalar slot) never fits. */
|
|
264
|
+
static inline sp_bool sp_bm_sig_pos_scalar_ok(sp_int code, const char *sig, sp_int i) {
|
|
265
|
+
if (!sig || code == 0) return FALSE;
|
|
266
|
+
sp_int want = 0;
|
|
267
|
+
for (int k = 0; k < 8; k++) want = want * 10 + (sig[8 * i + k] - '0');
|
|
268
|
+
return want == code || want == 0;
|
|
269
|
+
}
|
|
270
|
+
/* Whether a call passing `argc` arguments whose per-position ABI type tokens
|
|
271
|
+
(eight chars each; see abi_sig_token in codegen_call.c) are `arg_sig` can
|
|
272
|
+
ride the target's legacy sp_int ABI.
|
|
273
|
+
The count must fit the target's fixed signature and every fixed position's
|
|
274
|
+
C type must match exactly: a bool parameter is never handed an int, a
|
|
275
|
+
String* is never handed an IntArray*, and an sp_Foo* is never handed an
|
|
276
|
+
sp_Bar*. An optional parameter is only safe at full arity (the C signature
|
|
277
|
+
reads every fixed slot), so a shorter call declines. Declines a non-callable
|
|
278
|
+
target, an unbound Method, and a non-int return (legacy_int_abi == 0). */
|
|
279
|
+
static inline sp_bool sp_bm_sig_pos_match(const char *a, const char *b) {
|
|
280
|
+
if (memcmp(a, b, 8) == 0) return TRUE;
|
|
281
|
+
/* 0 is the TY_UNKNOWN wildcard: it accepts any scalar-kind token, and a
|
|
282
|
+
scalar-kind arg accepts an unknown parameter. Pointer tokens carry the
|
|
283
|
+
100000 offset, so they never match the wildcard. */
|
|
284
|
+
sp_int va = 0, vb = 0;
|
|
285
|
+
for (int i = 0; i < 8; i++) { va = va * 10 + (a[i] - '0'); vb = vb * 10 + (b[i] - '0'); }
|
|
286
|
+
if (va == 0) return vb < 100000;
|
|
287
|
+
if (vb == 0) return va < 100000;
|
|
288
|
+
return FALSE;
|
|
289
|
+
}
|
|
290
|
+
/* Whether a call passing `argc` arguments can ride the target's promote poly
|
|
291
|
+
ABI (every slot an sp_RbVal): the stamp must be set, the Method bound, and
|
|
292
|
+
the count exactly the fixed slot count the C signature reads. */
|
|
293
|
+
static inline sp_bool sp_bm_poly_abi_ok(sp_BoundMethod *m, sp_int argc) {
|
|
294
|
+
return m && !m->unbound && m->poly_abi && argc == m->poly_fixed;
|
|
295
|
+
}
|
|
296
|
+
static inline sp_bool sp_bm_legacy_abi_ok(sp_BoundMethod *m, sp_int argc, const char *arg_sig) {
|
|
297
|
+
if (!m || m->unbound || !m->legacy_int_abi || !m->legacy_sig || !arg_sig) return FALSE;
|
|
298
|
+
if (m->legacy_rest) { if (argc < m->legacy_fixed) return FALSE; }
|
|
299
|
+
else if (argc != m->legacy_fixed) return FALSE;
|
|
300
|
+
sp_int n = argc < m->legacy_fixed ? argc : m->legacy_fixed;
|
|
301
|
+
for (sp_int k = 0; k < n; k++)
|
|
302
|
+
if (!sp_bm_sig_pos_match(arg_sig + 8 * k, m->legacy_sig + 8 * k)) return FALSE;
|
|
303
|
+
return TRUE;
|
|
304
|
+
}
|
|
305
|
+
static inline sp_BoundMethod *sp_bound_method_new_d(void *self, sp_int self_kind, sp_int fn, const char *name, sp_int arity, const char *desc) { sp_BoundMethod *m = sp_bound_method_new(self, self_kind, fn, name, arity); m->desc = desc; return m; }
|
|
306
|
+
/* Method#unbind on a BOXED method: the same target with the receiver dropped.
|
|
307
|
+
A boxed value carries no syntax for the compile-time unbound rendering, so
|
|
308
|
+
the copy records it (the #class arm reads `unbound`) (#3692). */
|
|
309
|
+
static inline sp_BoundMethod *sp_bm_unbind(sp_BoundMethod *m) {
|
|
310
|
+
if (!m) return NULL;
|
|
311
|
+
sp_BoundMethod *u = sp_bound_method_new(NULL, SP_BM_SELF_NONE, m->fn, m->name, m->arity);
|
|
312
|
+
u->desc = m->desc;
|
|
313
|
+
u->unbound = 1;
|
|
314
|
+
u->legacy_int_abi = m->legacy_int_abi;
|
|
315
|
+
u->legacy_sig = m->legacy_sig;
|
|
316
|
+
u->legacy_fixed = m->legacy_fixed;
|
|
317
|
+
u->legacy_rest = m->legacy_rest;
|
|
318
|
+
u->legacy_ret = m->legacy_ret;
|
|
319
|
+
u->poly_abi = m->poly_abi;
|
|
320
|
+
u->poly_fixed = m->poly_fixed;
|
|
321
|
+
u->thunk = m->thunk;
|
|
322
|
+
u->thunk_min = m->thunk_min;
|
|
323
|
+
u->thunk_max = m->thunk_max;
|
|
324
|
+
return u;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#endif
|