qr_reader-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-mruby.txt +22 -0
  3. data/LICENSE-spinel.txt +19 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +61 -0
  6. data/ext/qr_reader_native/SPINEL_VERSION +1 -0
  7. data/ext/qr_reader_native/extconf.rb +8 -0
  8. data/ext/qr_reader_native/mruby/error.h +13 -0
  9. data/ext/qr_reader_native/mruby/internal.h +145 -0
  10. data/ext/qr_reader_native/mruby/string.h +22 -0
  11. data/ext/qr_reader_native/mruby.h +93 -0
  12. data/ext/qr_reader_native/mruby_shim.h +118 -0
  13. data/ext/qr_reader_native/qr_reader_native.c +13513 -0
  14. data/ext/qr_reader_native/qr_reader_native.h +29 -0
  15. data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
  16. data/ext/qr_reader_native/re_cased.h +71 -0
  17. data/ext/qr_reader_native/re_compile.c +5784 -0
  18. data/ext/qr_reader_native/re_ctype.h +3540 -0
  19. data/ext/qr_reader_native/re_exec.c +2190 -0
  20. data/ext/qr_reader_native/re_internal.h +671 -0
  21. data/ext/qr_reader_native/re_prop.h +4629 -0
  22. data/ext/qr_reader_native/re_spinel.c +363 -0
  23. data/ext/qr_reader_native/re_spinel.h +22 -0
  24. data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
  25. data/ext/qr_reader_native/re_utf8.c +156 -0
  26. data/ext/qr_reader_native/sp_alloc.c +1447 -0
  27. data/ext/qr_reader_native/sp_alloc.h +1196 -0
  28. data/ext/qr_reader_native/sp_argf.h +33 -0
  29. data/ext/qr_reader_native/sp_array.c +1052 -0
  30. data/ext/qr_reader_native/sp_array.h +456 -0
  31. data/ext/qr_reader_native/sp_bigint.c +5826 -0
  32. data/ext/qr_reader_native/sp_bigint.h +117 -0
  33. data/ext/qr_reader_native/sp_cold.c +5259 -0
  34. data/ext/qr_reader_native/sp_compat.h +402 -0
  35. data/ext/qr_reader_native/sp_core.c +818 -0
  36. data/ext/qr_reader_native/sp_core.h +56 -0
  37. data/ext/qr_reader_native/sp_crypto.c +877 -0
  38. data/ext/qr_reader_native/sp_crypto.h +169 -0
  39. data/ext/qr_reader_native/sp_dtoa.c +1533 -0
  40. data/ext/qr_reader_native/sp_dtoa.h +29 -0
  41. data/ext/qr_reader_native/sp_encoding_names.h +172 -0
  42. data/ext/qr_reader_native/sp_enum.h +86 -0
  43. data/ext/qr_reader_native/sp_exc.c +1185 -0
  44. data/ext/qr_reader_native/sp_exc.h +186 -0
  45. data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
  46. data/ext/qr_reader_native/sp_fiber.c +889 -0
  47. data/ext/qr_reader_native/sp_fiber.h +162 -0
  48. data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
  49. data/ext/qr_reader_native/sp_format.c +695 -0
  50. data/ext/qr_reader_native/sp_format.h +83 -0
  51. data/ext/qr_reader_native/sp_gc.c +1793 -0
  52. data/ext/qr_reader_native/sp_gc.h +762 -0
  53. data/ext/qr_reader_native/sp_hash.c +131 -0
  54. data/ext/qr_reader_native/sp_hash.h +131 -0
  55. data/ext/qr_reader_native/sp_inspect.c +215 -0
  56. data/ext/qr_reader_native/sp_inspect.h +128 -0
  57. data/ext/qr_reader_native/sp_io.c +1676 -0
  58. data/ext/qr_reader_native/sp_io.h +272 -0
  59. data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
  60. data/ext/qr_reader_native/sp_iobuffer.h +167 -0
  61. data/ext/qr_reader_native/sp_marshal.c +487 -0
  62. data/ext/qr_reader_native/sp_marshal.h +60 -0
  63. data/ext/qr_reader_native/sp_net.c +1029 -0
  64. data/ext/qr_reader_native/sp_net.h +205 -0
  65. data/ext/qr_reader_native/sp_pack.c +1254 -0
  66. data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
  67. data/ext/qr_reader_native/sp_proc.c +410 -0
  68. data/ext/qr_reader_native/sp_proc.h +327 -0
  69. data/ext/qr_reader_native/sp_process.c +547 -0
  70. data/ext/qr_reader_native/sp_process_status.c +211 -0
  71. data/ext/qr_reader_native/sp_process_status.h +66 -0
  72. data/ext/qr_reader_native/sp_random.c +287 -0
  73. data/ext/qr_reader_native/sp_random.h +52 -0
  74. data/ext/qr_reader_native/sp_range.h +144 -0
  75. data/ext/qr_reader_native/sp_re.c +1379 -0
  76. data/ext/qr_reader_native/sp_re.h +186 -0
  77. data/ext/qr_reader_native/sp_re_names.h +59 -0
  78. data/ext/qr_reader_native/sp_sched.c +4044 -0
  79. data/ext/qr_reader_native/sp_sched.h +295 -0
  80. data/ext/qr_reader_native/sp_slab.c +1507 -0
  81. data/ext/qr_reader_native/sp_str.c +1462 -0
  82. data/ext/qr_reader_native/sp_str.h +306 -0
  83. data/ext/qr_reader_native/sp_str_crypt.c +23 -0
  84. data/ext/qr_reader_native/sp_string.c +92 -0
  85. data/ext/qr_reader_native/sp_string.h +253 -0
  86. data/ext/qr_reader_native/sp_system.c +78 -0
  87. data/ext/qr_reader_native/sp_system.h +18 -0
  88. data/ext/qr_reader_native/sp_time.c +860 -0
  89. data/ext/qr_reader_native/sp_time.h +100 -0
  90. data/ext/qr_reader_native/sp_types.h +292 -0
  91. data/ext/qr_reader_native/spinel_rt.h +19017 -0
  92. data/ext/qr_reader_native/unicase.c +399 -0
  93. data/ext/qr_reader_native/unicase.h +901 -0
  94. data/lib/qr_reader/native.rb +6 -0
  95. metadata +153 -0
@@ -0,0 +1,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