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,1220 @@
|
|
|
1
|
+
/* lib/sp_poly_cold.c -- the cold half of the boxed-value runtime.
|
|
2
|
+
|
|
3
|
+
These functions (inspect, to_s, puts, dup, slice, merge, clear, the keyword
|
|
4
|
+
checks, ...) used to be `static` in spinel_rt.h, so every generated
|
|
5
|
+
translation unit parsed and discarded them. They are compiled once here and
|
|
6
|
+
the header declares them. This unit includes the header as a host
|
|
7
|
+
(SPINEL_EXT_HOST), so the state the generated unit owns -- the exception
|
|
8
|
+
stack, the pending-exception slots, ARGV -- stays extern here, and the header's
|
|
9
|
+
constructors (which hand the collector THIS unit's hooks) are turned off.
|
|
10
|
+
Nothing in this file may keep mutable state of its own: the nm check in
|
|
11
|
+
`make poly-cold-test` fails when a writable local symbol appears. The symbol
|
|
12
|
+
and class-name tables are generated per program; they are reached through the
|
|
13
|
+
hooks the generated unit installs (sp_sym_name_fn, sp_class_name_fn,
|
|
14
|
+
sp_json_sym_intern_fn). */
|
|
15
|
+
#define SPINEL_EXT_HOST 1
|
|
16
|
+
#include "sp_compat.h"
|
|
17
|
+
#undef SP_CONSTRUCTOR
|
|
18
|
+
#define SP_CONSTRUCTOR SP_UNUSED
|
|
19
|
+
#define sp_sym_to_s sp_poly_cold_sym_to_s
|
|
20
|
+
#define sp_class_to_s sp_poly_cold_class_to_s
|
|
21
|
+
#define sp_sym_intern sp_poly_cold_sym_intern
|
|
22
|
+
#include "spinel_rt.h"
|
|
23
|
+
|
|
24
|
+
const char *(*sp_class_name_fn)(sp_Class);
|
|
25
|
+
/* the hooks the generated unit installs for this unit (SP_INSTALL_HOOK) */
|
|
26
|
+
sp_user_binop_fn sp_user_binop_hook_lib;
|
|
27
|
+
sp_obj_eq_fn sp_obj_eq_hook_lib;
|
|
28
|
+
sp_obj_hash_fn sp_obj_hash_hook_lib;
|
|
29
|
+
sp_obj_eql_fn sp_obj_eql_hook_lib;
|
|
30
|
+
void (*sp_user_init_copy_hook_lib)(sp_RbVal, sp_RbVal);
|
|
31
|
+
sp_RbVal (*sp_bsub_dup_hook_lib)(sp_RbVal, int, sp_bool *);
|
|
32
|
+
/* the symbol interner is generated per program too; a keyword name that is not
|
|
33
|
+
a known symbol reaches it through the hook the generated unit installs */
|
|
34
|
+
sp_sym sp_poly_cold_sym_intern(const char *s) { return sp_json_sym_intern_fn ? sp_json_sym_intern_fn(s) : (sp_sym)0; }
|
|
35
|
+
const char *sp_poly_cold_sym_to_s(sp_sym id) { return sp_sym_name_fn ? sp_sym_name_fn(id) : sp_str_empty; }
|
|
36
|
+
const char *sp_poly_cold_class_to_s(sp_Class c) {
|
|
37
|
+
if (c.name) return c.name;
|
|
38
|
+
return sp_class_name_fn ? sp_class_name_fn(c) : sp_str_empty;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const char *sp_sym_inspect(sp_sym id)
|
|
42
|
+
{ if (id == (sp_sym)-1) return SPL("nil"); /* nilable-symbol sentinel */ return sp_sym_inspect_name(sp_sym_to_s(id)); }
|
|
43
|
+
|
|
44
|
+
void sp_poly_puts(sp_RbVal v)
|
|
45
|
+
{
|
|
46
|
+
switch (v.tag) {
|
|
47
|
+
case SP_TAG_INT: printf("%lld\n", (long long)v.v.i); break;
|
|
48
|
+
case SP_TAG_STR: if (v.v.s) {
|
|
49
|
+
size_t _n = sp_str_byte_len(v.v.s);
|
|
50
|
+
fwrite(v.v.s, 1, _n, stdout);
|
|
51
|
+
if (!_n || v.v.s[_n - 1] != '\n') putchar('\n');
|
|
52
|
+
}
|
|
53
|
+
else putchar('\n'); break;
|
|
54
|
+
case SP_TAG_FLT: { fputs(sp_float_to_s(v.v.f), stdout); putchar('\n'); break; }
|
|
55
|
+
case SP_TAG_BOOL: puts(v.v.b ? "true" : "false"); break;
|
|
56
|
+
case SP_TAG_NIL: putchar('\n'); break;
|
|
57
|
+
case SP_TAG_SYM: { const char *_ss = sp_sym_to_s((sp_sym)v.v.i); fputs(_ss, stdout); putchar('\n'); break; }
|
|
58
|
+
case SP_TAG_ENCODING: { const char *_es = v.v.s ? v.v.s : sp_str_empty; fputs(_es, stdout); putchar('\n'); break; }
|
|
59
|
+
case SP_TAG_CLASS: { fputs(sp_class_val_name(v), stdout); putchar('\n'); break; }
|
|
60
|
+
case SP_TAG_BIGINT: { const char *_bs = sp_bigint_to_s((sp_Bigint *)v.v.p); if (_bs) fputs(_bs, stdout); putchar('\n'); break; }
|
|
61
|
+
case SP_TAG_OBJ: {
|
|
62
|
+
/* MRI's `puts arr` iterates an Array, printing one element per
|
|
63
|
+
line (using to_s on each); a non-Array OBJ falls back to
|
|
64
|
+
inspect / class-name. */
|
|
65
|
+
switch (v.cls_id) {
|
|
66
|
+
case SP_BUILTIN_INT_ARRAY: {
|
|
67
|
+
sp_IntArray *_a = (sp_IntArray *)v.v.p;
|
|
68
|
+
for (sp_int _i = 0; _i < _a->len; _i++)
|
|
69
|
+
printf("%lld\n", (long long)_a->data[_a->start + _i]);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case SP_BUILTIN_FLT_ARRAY: {
|
|
73
|
+
sp_FloatArray *_a = (sp_FloatArray *)v.v.p;
|
|
74
|
+
for (sp_int _i = 0; _i < _a->len; _i++) {
|
|
75
|
+
fputs(sp_float_to_s(_a->data[_i]), stdout); putchar('\n');
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case SP_BUILTIN_STR_ARRAY: {
|
|
80
|
+
sp_StrArray *_a = (sp_StrArray *)v.v.p;
|
|
81
|
+
for (sp_int _i = 0; _i < _a->len; _i++) {
|
|
82
|
+
const char *_s = _a->data[_i];
|
|
83
|
+
if (_s) {
|
|
84
|
+
size_t _n = sp_str_byte_len(_s);
|
|
85
|
+
fwrite(_s, 1, _n, stdout);
|
|
86
|
+
if (!_n || _s[_n - 1] != '\n') putchar('\n');
|
|
87
|
+
}
|
|
88
|
+
else putchar('\n');
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case SP_BUILTIN_SYM_ARRAY: {
|
|
93
|
+
sp_IntArray *_a = (sp_IntArray *)v.v.p;
|
|
94
|
+
for (sp_int _i = 0; _i < _a->len; _i++) {
|
|
95
|
+
const char *_s = sp_sym_to_s((sp_sym)_a->data[_a->start + _i]);
|
|
96
|
+
fputs(_s, stdout); putchar('\n');
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case SP_BUILTIN_RANGE: puts(sp_range_str(*(sp_Range *)v.v.p)); break;
|
|
101
|
+
case SP_BUILTIN_FLOAT_RANGE: puts(sp_frange_inspect(*(sp_FloatRange *)v.v.p)); break;
|
|
102
|
+
case SP_BUILTIN_STR_RANGE: puts(sp_srange_to_s(*(sp_StrRange *)v.v.p)); break;
|
|
103
|
+
case SP_BUILTIN_TIME: puts(sp_Time_to_s((sp_Time *)v.v.p)); break;
|
|
104
|
+
case SP_BUILTIN_STRBUF: puts(sp_String_cstr((sp_String *)v.v.p)); break;
|
|
105
|
+
case SP_BUILTIN_COMPLEX: puts(sp_complex_to_s(*(sp_Complex *)v.v.p)); break;
|
|
106
|
+
case SP_BUILTIN_RATIONAL: puts(sp_rational_to_s(*(sp_Rational *)v.v.p)); break;
|
|
107
|
+
case SP_BUILTIN_BIG_RATIONAL: puts(sp_brat_to_s((sp_BigRational *)v.v.p)); break;
|
|
108
|
+
case SP_BUILTIN_REGEX: puts(sp_re_to_s_str(v.v.p)); break;
|
|
109
|
+
case SP_BUILTIN_PTR_ARRAY: { /* rows or objects, one per line like any array (#4486) */
|
|
110
|
+
sp_PtrArray *_pa = (sp_PtrArray *)v.v.p;
|
|
111
|
+
if (_pa) for (sp_int _i = 0; _i < _pa->len; _i++) sp_poly_puts(sp_PtrArray_elem_box(_pa, _pa->data[_i]));
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case SP_BUILTIN_POLY_ARRAY: {
|
|
115
|
+
/* puts flattens arrays recursively, one element per line -- and one
|
|
116
|
+
that holds itself gets CRuby's single `[...]` line instead of an
|
|
117
|
+
endless flattening. */
|
|
118
|
+
sp_PolyArray *_a = (sp_PolyArray *)v.v.p;
|
|
119
|
+
if (sp_poly_recur_seen(SP_POLY_RECUR_PUTS, _a, NULL)) { puts("[...]"); break; }
|
|
120
|
+
int _pm = sp_poly_recur_push(SP_POLY_RECUR_PUTS, _a, NULL);
|
|
121
|
+
for (sp_int _i = 0; _i < _a->len; _i++) sp_poly_puts(_a->data[_i]);
|
|
122
|
+
sp_poly_recur_pop(_pm);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
/* A user object (or any non-array OBJ) prints via to_s: delegate to
|
|
126
|
+
sp_poly_to_s, which dispatches the class's user #to_s (falling back to
|
|
127
|
+
the default #<Name:0x..>). The bare #<Object> print skipped the user
|
|
128
|
+
to_s for an object read from a collection (#3189). */
|
|
129
|
+
default: { fputs(sp_poly_to_s(v), stdout); putchar('\n'); break; }
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
default: printf("%lld\n", (long long)v.v.i); break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const char *sp_poly_to_s(sp_RbVal v)
|
|
138
|
+
{
|
|
139
|
+
switch (v.tag) {
|
|
140
|
+
/* int-typed nil (SP_INT_NIL) is Ruby nil; nil.to_s is "" -- match it. */
|
|
141
|
+
case SP_TAG_INT: return v.v.i == SP_INT_NIL ? sp_str_frozen_empty : sp_int_to_s(v.v.i);
|
|
142
|
+
case SP_TAG_STR: return v.v.s ? v.v.s : sp_str_frozen_empty;
|
|
143
|
+
case SP_TAG_FLT: return sp_float_to_s(v.v.f);
|
|
144
|
+
case SP_TAG_BOOL: return v.v.b ? sp_str_frozen_true : sp_str_frozen_false;
|
|
145
|
+
case SP_TAG_NIL: return sp_str_frozen_empty;
|
|
146
|
+
case SP_TAG_SYM: return sp_sym_to_s_chilled((sp_sym)v.v.i);
|
|
147
|
+
case SP_TAG_CLASS: return sp_class_val_name(v);
|
|
148
|
+
case SP_TAG_ENCODING: return v.v.s ? v.v.s : sp_str_empty;
|
|
149
|
+
case SP_TAG_BIGINT: return sp_bigint_to_s((sp_Bigint *)v.v.p);
|
|
150
|
+
case SP_TAG_OBJ:
|
|
151
|
+
switch (v.cls_id) {
|
|
152
|
+
case SP_BUILTIN_INT_ARRAY: return sp_IntArray_inspect((sp_IntArray *)v.v.p);
|
|
153
|
+
case SP_BUILTIN_FLT_ARRAY: return sp_FloatArray_inspect((sp_FloatArray *)v.v.p);
|
|
154
|
+
case SP_BUILTIN_STR_ARRAY: return sp_StrArray_inspect((sp_StrArray *)v.v.p);
|
|
155
|
+
case SP_BUILTIN_SYM_ARRAY: return sp_SymArray_inspect((sp_IntArray *)v.v.p);
|
|
156
|
+
case SP_BUILTIN_PTR_ARRAY: return sp_PtrArray_inspect_k((sp_PtrArray *)v.v.p);
|
|
157
|
+
/* Array#to_s is Array#inspect; a boxed PolyArray element had no arm
|
|
158
|
+
and fell through to "" (#3007) */
|
|
159
|
+
case SP_BUILTIN_POLY_ARRAY: return sp_PolyArray_inspect((sp_PolyArray *)v.v.p);
|
|
160
|
+
case SP_BUILTIN_RANGE: return sp_range_str(*(sp_Range *)v.v.p);
|
|
161
|
+
case SP_BUILTIN_FLOAT_RANGE: return sp_frange_inspect(*(sp_FloatRange *)v.v.p);
|
|
162
|
+
case SP_BUILTIN_STR_RANGE: return sp_srange_to_s(*(sp_StrRange *)v.v.p);
|
|
163
|
+
case SP_BUILTIN_TIME: return sp_Time_to_s((sp_Time *)v.v.p);
|
|
164
|
+
case SP_BUILTIN_STRBUF: return sp_String_cstr((sp_String *)v.v.p); /* live buffer (#3227) */
|
|
165
|
+
case SP_BUILTIN_METHOD: return sp_method_desc_cstr((sp_BoundMethod *)v.v.p);
|
|
166
|
+
case SP_BUILTIN_COMPLEX: return sp_complex_to_s(*(sp_Complex *)v.v.p);
|
|
167
|
+
case SP_BUILTIN_RATIONAL: return sp_rational_to_s(*(sp_Rational *)v.v.p);
|
|
168
|
+
case SP_BUILTIN_BIG_RATIONAL: return sp_brat_to_s((sp_BigRational *)v.v.p);
|
|
169
|
+
case SP_BUILTIN_REGEX: return sp_re_to_s_str(v.v.p);
|
|
170
|
+
/* MatchData#to_s is the whole match (#3641) */
|
|
171
|
+
case SP_BUILTIN_MATCHDATA: { const char *_m0 = sp_MatchData_aref((sp_MatchData *)v.v.p, 0); return _m0 ? _m0 : sp_str_empty; }
|
|
172
|
+
case SP_BUILTIN_EXCEPTION: return sp_exc_message((volatile struct sp_Exception_s *)v.v.p);
|
|
173
|
+
/* Object#to_s on a boxed handle of the IO family, as the typed arm
|
|
174
|
+
renders it: a boxed one fell through to "" (`puts [f, d]`) */
|
|
175
|
+
case SP_BUILTIN_IO: return sp_io_to_s((sp_File *)v.v.p);
|
|
176
|
+
case SP_BUILTIN_DIR: return sp_Dir_to_s((sp_Dir *)v.v.p);
|
|
177
|
+
case SP_BUILTIN_FIBER: return sp_Fiber_inspect((sp_Fiber *)v.v.p);
|
|
178
|
+
case SP_BUILTIN_THREAD: return sp_Thread_inspect((sp_thread *)v.v.p);
|
|
179
|
+
/* OpenStruct#to_s is its inspect, as sp_poly_inspect renders it */
|
|
180
|
+
case SP_BUILTIN_OPENSTRUCT: return sp_OpenStruct_inspect((struct sp_OpenStruct_s *)v.v.p);
|
|
181
|
+
default:
|
|
182
|
+
if ((v.cls_id >= 0 || v.cls_id == SP_BUILTIN_OBJECT) && v.v.p) {
|
|
183
|
+
/* a class with a user #to_s renders through the generated
|
|
184
|
+
dispatcher; the rest (bare Object.new included) get CRuby's
|
|
185
|
+
default #<Name:0xADDR> */
|
|
186
|
+
if (v.cls_id >= 0 && sp_obj_to_s_fn) {
|
|
187
|
+
/* rooted across the user #to_s, which allocates: the boxed
|
|
188
|
+
copy may be a fresh object's only reference (`puts(k ?
|
|
189
|
+
C.new : 1)`), as in sp_poly_check_str_obj */
|
|
190
|
+
SP_GC_ROOT_RBVAL(v);
|
|
191
|
+
const char *us = sp_obj_to_s_fn(v.cls_id, v.v.p);
|
|
192
|
+
if (us) return us;
|
|
193
|
+
}
|
|
194
|
+
/* a user exception subclass boxes under its own class id */
|
|
195
|
+
if (sp_is_exc_subclass_cls(v.cls_id))
|
|
196
|
+
return sp_exc_message((volatile struct sp_Exception_s *)v.v.p);
|
|
197
|
+
if (v.v.p == sp_main_obj) return SPL("main");
|
|
198
|
+
return sp_sprintf("#<%s:0x%016llx>", sp_poly_class_name(v),
|
|
199
|
+
(unsigned long long)(uintptr_t)v.v.p);
|
|
200
|
+
}
|
|
201
|
+
/* a builtin container kind with no explicit arm above (the hash
|
|
202
|
+
variants): to_s is its inspect */
|
|
203
|
+
if (sp_poly_is_hash_kind(v.cls_id)) return sp_poly_inspect(v);
|
|
204
|
+
return sp_str_empty;
|
|
205
|
+
}
|
|
206
|
+
default: return sp_str_empty;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const char *sp_poly_class_name(sp_RbVal v)
|
|
211
|
+
{
|
|
212
|
+
switch (v.tag) {
|
|
213
|
+
case SP_TAG_INT: return SPL("Integer");
|
|
214
|
+
case SP_TAG_STR: return SPL("String");
|
|
215
|
+
case SP_TAG_FLT: return SPL("Float");
|
|
216
|
+
case SP_TAG_BOOL: return v.v.b ? SPL("TrueClass") : SPL("FalseClass");
|
|
217
|
+
case SP_TAG_NIL: return SPL("NilClass");
|
|
218
|
+
case SP_TAG_SYM: return SPL("Symbol");
|
|
219
|
+
case SP_TAG_ENCODING: return SPL("Encoding");
|
|
220
|
+
case SP_TAG_CLASS: {
|
|
221
|
+
sp_Class c = sp_unbox_class(v);
|
|
222
|
+
int m = sp_class_is_module_fn ? sp_class_is_module_fn(c)
|
|
223
|
+
: (c.cls_id == -114 || c.cls_id == -115 || c.cls_id == -119 || c.cls_id == -130 ||
|
|
224
|
+
c.cls_id == -162);
|
|
225
|
+
return m ? SPL("Module") : SPL("Class");
|
|
226
|
+
}
|
|
227
|
+
case SP_TAG_BIGINT: return SPL("Integer");
|
|
228
|
+
case SP_TAG_OBJ:
|
|
229
|
+
/* an Array subclass instance boxed as its Array (#7449) */
|
|
230
|
+
if (sp_bsub_cls_fn && sp_obj_cls_name_fn) {
|
|
231
|
+
int k = sp_bsub_cls_fn(v);
|
|
232
|
+
if (k >= 0) return sp_obj_cls_name_fn(k);
|
|
233
|
+
}
|
|
234
|
+
switch (v.cls_id) {
|
|
235
|
+
case SP_BUILTIN_INT_ARRAY: case SP_BUILTIN_FLT_ARRAY:
|
|
236
|
+
case SP_BUILTIN_STR_ARRAY: case SP_BUILTIN_SYM_ARRAY:
|
|
237
|
+
case SP_BUILTIN_PTR_ARRAY: case SP_BUILTIN_POLY_ARRAY: return SPL("Array");
|
|
238
|
+
case SP_BUILTIN_STR_INT_HASH: case SP_BUILTIN_STR_STR_HASH:
|
|
239
|
+
case SP_BUILTIN_INT_STR_HASH: case SP_BUILTIN_SYM_INT_HASH:
|
|
240
|
+
case SP_BUILTIN_INT_INT_HASH:
|
|
241
|
+
case SP_BUILTIN_SYM_STR_HASH: case SP_BUILTIN_STR_POLY_HASH:
|
|
242
|
+
case SP_BUILTIN_SYM_POLY_HASH: case SP_BUILTIN_POLY_POLY_HASH: return SPL("Hash");
|
|
243
|
+
case SP_BUILTIN_RANGE: return SPL("Range");
|
|
244
|
+
case SP_BUILTIN_FLOAT_RANGE: case SP_BUILTIN_STR_RANGE: return SPL("Range");
|
|
245
|
+
case SP_BUILTIN_TIME: return SPL("Time");
|
|
246
|
+
case SP_BUILTIN_STRBUF: return SPL("String"); /* (#3227) */
|
|
247
|
+
case SP_BUILTIN_COMPLEX: return SPL("Complex");
|
|
248
|
+
case SP_BUILTIN_RATIONAL: return SPL("Rational");
|
|
249
|
+
case SP_BUILTIN_BIG_RATIONAL: return SPL("Rational");
|
|
250
|
+
case SP_BUILTIN_REGEX: return SPL("Regexp");
|
|
251
|
+
case SP_BUILTIN_MATCHDATA: return SPL("MatchData"); /* (#3641) */
|
|
252
|
+
case SP_BUILTIN_OBJECT: return SPL("Object"); /* a bare Object.new instance */
|
|
253
|
+
case SP_BUILTIN_BASIC_OBJECT: return SPL("BasicObject");
|
|
254
|
+
case SP_BUILTIN_PROC: return SPL("Proc");
|
|
255
|
+
/* a curried proc IS a Proc to Ruby (#3885) */
|
|
256
|
+
case SP_BUILTIN_CURRY: return SPL("Proc");
|
|
257
|
+
case SP_BUILTIN_METHOD:
|
|
258
|
+
return ((sp_BoundMethod *)v.v.p)->unbound ? SPL("UnboundMethod") : SPL("Method"); /* (#3692) */
|
|
259
|
+
/* a chain and a product report their own class, as the typed
|
|
260
|
+
#class does */
|
|
261
|
+
case SP_BUILTIN_ENUMERATOR: return sp_enum_class_name(v.v.p);
|
|
262
|
+
case SP_BUILTIN_IO: {
|
|
263
|
+
/* the handle kind names the class, through the same authority the
|
|
264
|
+
typed .class emit uses -- a boxed socket must not report plain IO */
|
|
265
|
+
sp_File *_iof = (sp_File *)v.v.p;
|
|
266
|
+
if (_iof && _iof->mode &&
|
|
267
|
+
(strcmp(_iof->mode, "stat") == 0 || strcmp(_iof->mode, "lstat") == 0))
|
|
268
|
+
return SPL("File::Stat");
|
|
269
|
+
return sp_io_kind_name(_iof);
|
|
270
|
+
}
|
|
271
|
+
/* The concurrency handles: a boxed one reached the default arm and
|
|
272
|
+
answered an empty name, so `(:ok && queue).class` printed nothing
|
|
273
|
+
and a method on it raised NoMethodError with a blank class (#3484).
|
|
274
|
+
Mutex and Queue name themselves through the same helpers the typed
|
|
275
|
+
`.class` emit uses, so a Monitor and a SizedQueue stay distinct. */
|
|
276
|
+
case SP_BUILTIN_MUTEX: return sp_Mutex_class_name((sp_mutex *)v.v.p);
|
|
277
|
+
case SP_BUILTIN_QUEUE: return sp_Queue_class_name((sp_queue *)v.v.p);
|
|
278
|
+
case SP_BUILTIN_CONDVAR: return SPL("Thread::ConditionVariable");
|
|
279
|
+
case SP_BUILTIN_FIBER: return SPL("Fiber");
|
|
280
|
+
case SP_BUILTIN_THREAD: return SPL("Thread");
|
|
281
|
+
case SP_BUILTIN_DIR: return SPL("Dir");
|
|
282
|
+
case SP_BUILTIN_TMS: return SPL("Process::Tms");
|
|
283
|
+
case SP_BUILTIN_OPENSTRUCT: return SPL("OpenStruct");
|
|
284
|
+
/* These two had no arm of their own. They went unnoticed because their
|
|
285
|
+
cls_ids collided with STRBUF and OPENSTRUCT, so a boxed one answered
|
|
286
|
+
String / OpenStruct rather than nothing (#4158). */
|
|
287
|
+
case SP_BUILTIN_ADDRINFO: return SPL("Addrinfo");
|
|
288
|
+
case SP_BUILTIN_SOCKOPT: return SPL("Socket::Option");
|
|
289
|
+
case SP_BUILTIN_PROCESS_STATUS: return SPL("Process::Status");
|
|
290
|
+
case SP_BUILTIN_YIELDER: return SPL("Enumerator::Yielder");
|
|
291
|
+
case SP_BUILTIN_RANDOM: return SPL("Random");
|
|
292
|
+
case SP_BUILTIN_ARGF: return SPL("ARGF.class");
|
|
293
|
+
case SP_BUILTIN_EXCEPTION: return sp_exc_class_name((volatile struct sp_Exception_s *)v.v.p);
|
|
294
|
+
default: { sp_Class c = {v.cls_id}; return sp_class_to_s(c); }
|
|
295
|
+
}
|
|
296
|
+
default: return SPL("Object");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
sp_RbVal sp_poly_slice(sp_RbVal a, sp_int start, sp_int len)
|
|
301
|
+
{
|
|
302
|
+
if (a.tag == SP_TAG_STR) return sp_box_nullable_str(sp_str_sub_range(a.v.s ? a.v.s : "", start, len));
|
|
303
|
+
/* A shared-string handle is a String: slicing is non-mutating, so it answers
|
|
304
|
+
as its live value rather than falling through to the array kinds and out
|
|
305
|
+
the nil default, which is what `s = +""; s << "abc"; s[0, 2]` did (#4279). */
|
|
306
|
+
if (sp_poly_is_strbuf(a)) return sp_poly_slice(sp_poly_strbuf_deref(a), start, len);
|
|
307
|
+
/* An Integer answers `n[start, len]`, the len-bit field starting at bit
|
|
308
|
+
`start` -- the two-argument form of the bit read, which the typed arms
|
|
309
|
+
have had all along. A boxed receiver fell past this to the nil default
|
|
310
|
+
below, so `[255, nil][0][0, 4]` was nil where CRuby says 15, and the
|
|
311
|
+
value went on being used as a number (#4742). The int-typed nil keeps
|
|
312
|
+
the default: `nil[0, 4]` is a missing method, not a bit field. */
|
|
313
|
+
if (a.tag == SP_TAG_INT) {
|
|
314
|
+
/* the sentinel IS nil, and nil has no `[]` */
|
|
315
|
+
if (a.v.i == SP_INT_NIL) sp_raise_poly_nomethod("[]", sp_box_nil());
|
|
316
|
+
return sp_box_int(sp_int_bit_range(a.v.i, start, len));
|
|
317
|
+
}
|
|
318
|
+
if (a.tag == SP_TAG_BIGINT) {
|
|
319
|
+
sp_Bigint *n = (sp_Bigint *)a.v.p;
|
|
320
|
+
if (!n) return sp_box_nil();
|
|
321
|
+
if (start < 0 || len < 0) return sp_box_int(0);
|
|
322
|
+
{ sp_Bigint *sh = sp_bigint_shr(n, (int64_t)start);
|
|
323
|
+
sp_Bigint *m = sp_bigint_sub(sp_bigint_shl(sp_bigint_new_int(1), (int64_t)len),
|
|
324
|
+
sp_bigint_new_int(1));
|
|
325
|
+
return sp_box_int(sp_bigint_to_int(sp_bigint_and(sh, m))); }
|
|
326
|
+
}
|
|
327
|
+
/* A Symbol has `[]`: it answers its NAME sliced, as a String. Falling to
|
|
328
|
+
the default below made `[:sym, nil][0][0, 4]` nil, which then went on
|
|
329
|
+
being used as one. */
|
|
330
|
+
if (a.tag == SP_TAG_SYM)
|
|
331
|
+
return sp_box_nullable_str(sp_str_sub_range(sp_sym_to_s((sp_sym)a.v.i), start, len));
|
|
332
|
+
/* Everything else here -- nil, a Float, true, false -- simply has no `[]`,
|
|
333
|
+
and CRuby says so. The default used to answer nil for all of them, so a
|
|
334
|
+
receiver that was never sliceable produced a value indistinguishable
|
|
335
|
+
from an in-range miss, and the program carried it forward. (The Integer
|
|
336
|
+
arm above notes the same thing about nil: a missing method, not a bit
|
|
337
|
+
field.) */
|
|
338
|
+
if (a.tag != SP_TAG_OBJ) sp_raise_poly_nomethod("[]", a);
|
|
339
|
+
/* arr[start, negative] is nil in CRuby (the slice helpers would return []) */
|
|
340
|
+
if (len < 0 && sp_poly_is_array_kind(a.cls_id)) return sp_box_nil();
|
|
341
|
+
/* bm[a, b]: a boxed bound Method called with two int arguments (optcarrot's
|
|
342
|
+
store dispatch table: `@store[addr][addr, value]`). The raw fn cast is
|
|
343
|
+
only valid for a target whose stamped per-position ABI accepts a scalar
|
|
344
|
+
at every fixed position (sp_bm_legacy_abi_ok); anything else (a
|
|
345
|
+
rest/optional/keyword signature, a float/poly/struct parameter, or a
|
|
346
|
+
pointer parameter the integer operands cannot fill) would read the wrong
|
|
347
|
+
C types, so route it through the callable helper, which raises CRuby's
|
|
348
|
+
NoMethodError instead of crashing (#4395). */
|
|
349
|
+
if (a.cls_id == SP_BUILTIN_METHOD) {
|
|
350
|
+
sp_BoundMethod *m = (sp_BoundMethod *)a.v.p;
|
|
351
|
+
/* A poly-ABI target (stamped at bind time; the promote signature) takes
|
|
352
|
+
the two operands boxed and answers boxed. Checked ahead of the legacy
|
|
353
|
+
gate: under promote the legacy stamp is 0, and elsewhere the two are
|
|
354
|
+
mutually exclusive by construction. */
|
|
355
|
+
if (m->fn && sp_bm_poly_abi_ok(m, 2)) {
|
|
356
|
+
sp_RbVal _a = sp_box_int(start), _b = sp_box_int(len);
|
|
357
|
+
if (m->legacy_ret == SP_BM_RET_POLY) {
|
|
358
|
+
if (m->recv_bound)
|
|
359
|
+
return ((sp_RbVal (*)(void *, sp_RbVal, sp_RbVal))(uintptr_t)m->fn)((void *)m->self, _a, _b);
|
|
360
|
+
return ((sp_RbVal (*)(sp_RbVal, sp_RbVal))(uintptr_t)m->fn)(_a, _b);
|
|
361
|
+
}
|
|
362
|
+
if (m->legacy_ret == SP_BM_RET_NIL) { /* a C void function: wasm checks the signature */
|
|
363
|
+
if (m->recv_bound) ((void (*)(void *, sp_RbVal, sp_RbVal))(uintptr_t)m->fn)((void *)m->self, _a, _b);
|
|
364
|
+
else ((void (*)(sp_RbVal, sp_RbVal))(uintptr_t)m->fn)(_a, _b);
|
|
365
|
+
return sp_box_nil();
|
|
366
|
+
}
|
|
367
|
+
if (m->recv_bound)
|
|
368
|
+
return sp_bm_box_ret(m, sp_bm_norm_ret(m, ((sp_int (*)(void *, sp_RbVal, sp_RbVal))(uintptr_t)m->fn)((void *)m->self, _a, _b)));
|
|
369
|
+
return sp_bm_box_ret(m, sp_bm_norm_ret(m, ((sp_int (*)(sp_RbVal, sp_RbVal))(uintptr_t)m->fn)(_a, _b)));
|
|
370
|
+
}
|
|
371
|
+
/* The two operands are sp_int, so the target's fixed positions must be the
|
|
372
|
+
int token (or the TY_UNKNOWN wildcard), not some other scalar kind. */
|
|
373
|
+
if (sp_bm_legacy_abi_ok(m, 2, "0000000100000001")) {
|
|
374
|
+
/* A top-level method has no self; its C signature leads with the first
|
|
375
|
+
parameter, so the self-ful cast would shift both operands (#4395). A
|
|
376
|
+
poly return is an sp_RbVal in two registers, so it takes its own cast. */
|
|
377
|
+
if (m->legacy_ret == SP_BM_RET_POLY) {
|
|
378
|
+
if (m->recv_bound)
|
|
379
|
+
return ((sp_RbVal (*)(void *, sp_int, sp_int))(uintptr_t)m->fn)((void *)m->self, start, len);
|
|
380
|
+
return ((sp_RbVal (*)(sp_int, sp_int))(uintptr_t)m->fn)(start, len);
|
|
381
|
+
}
|
|
382
|
+
/* a nil-returning target is a C void function, and wasm checks the
|
|
383
|
+
callee's signature at the call (sp_method_proc_tramp has the same arm) */
|
|
384
|
+
if (m->legacy_ret == SP_BM_RET_NIL) {
|
|
385
|
+
if (m->recv_bound) ((void (*)(void *, sp_int, sp_int))(uintptr_t)m->fn)((void *)m->self, start, len);
|
|
386
|
+
else ((void (*)(sp_int, sp_int))(uintptr_t)m->fn)(start, len);
|
|
387
|
+
return sp_box_nil();
|
|
388
|
+
}
|
|
389
|
+
if (m->recv_bound)
|
|
390
|
+
return sp_bm_box_ret(m, ((sp_int (*)(void *, sp_int, sp_int))(uintptr_t)m->fn)((void *)m->self, start, len));
|
|
391
|
+
return sp_bm_box_ret(m, ((sp_int (*)(sp_int, sp_int))(uintptr_t)m->fn)(start, len));
|
|
392
|
+
}
|
|
393
|
+
_sp_proc_poly_args[0] = sp_box_int(start);
|
|
394
|
+
_sp_proc_poly_args[1] = sp_box_int(len);
|
|
395
|
+
sp_int slots[2]; slots[0] = start; slots[1] = len;
|
|
396
|
+
return sp_poly_callable_call(a, 2, slots);
|
|
397
|
+
}
|
|
398
|
+
/* Proc#[] is #call: a two-int slice on a callable is a two-argument call.
|
|
399
|
+
The emitter sends only statically-Integer operands here, so a boxed Proc
|
|
400
|
+
previously fell into the array switch and answered nil (#4395). */
|
|
401
|
+
if (a.v.p && (a.cls_id == SP_BUILTIN_PROC || a.cls_id == SP_BUILTIN_CURRY)) {
|
|
402
|
+
_sp_proc_poly_args[0] = sp_box_int(start);
|
|
403
|
+
_sp_proc_poly_args[1] = sp_box_int(len);
|
|
404
|
+
sp_int slots[16];
|
|
405
|
+
slots[0] = start;
|
|
406
|
+
slots[1] = len;
|
|
407
|
+
return sp_poly_callable_call(a, 2, slots);
|
|
408
|
+
}
|
|
409
|
+
switch (a.cls_id) {
|
|
410
|
+
case SP_BUILTIN_INT_ARRAY: return sp_box_int_array(sp_IntArray_slice((sp_IntArray*)a.v.p, start, len));
|
|
411
|
+
case SP_BUILTIN_FLT_ARRAY: return sp_box_float_array(sp_FloatArray_slice((sp_FloatArray*)a.v.p, start, len));
|
|
412
|
+
case SP_BUILTIN_STR_ARRAY: return sp_box_str_array(sp_StrArray_slice((sp_StrArray*)a.v.p, start, len));
|
|
413
|
+
case SP_BUILTIN_POLY_ARRAY: return sp_box_poly_array(sp_PolyArray_slice((sp_PolyArray*)a.v.p, start, len));
|
|
414
|
+
/* a slice of a pointer array is a fresh general Array of the same rows or
|
|
415
|
+
objects, by reference (#4486) */
|
|
416
|
+
case SP_BUILTIN_PTR_ARRAY: return sp_box_poly_array(sp_PolyArray_slice(sp_PtrArray_to_poly((sp_PtrArray*)a.v.p), start, len));
|
|
417
|
+
default: return sp_box_nil();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const char *sp_poly_inspect(sp_RbVal v)
|
|
422
|
+
{
|
|
423
|
+
switch (v.tag) {
|
|
424
|
+
/* An int-typed nil (unfilled int block param, nullable-int miss) carries
|
|
425
|
+
the SP_INT_NIL sentinel; render it as nil, not the raw INT64_MIN. */
|
|
426
|
+
case SP_TAG_INT: return v.v.i == SP_INT_NIL ? SPL("nil") : sp_int_to_s(v.v.i);
|
|
427
|
+
case SP_TAG_STR: return sp_str_inspect(v.v.s);
|
|
428
|
+
case SP_TAG_FLT: return sp_float_to_s(v.v.f);
|
|
429
|
+
/* true.inspect is true.to_s, the frozen one; nil.inspect is a new "nil" */
|
|
430
|
+
case SP_TAG_BOOL: return v.v.b ? sp_str_frozen_true : sp_str_frozen_false;
|
|
431
|
+
case SP_TAG_NIL: return SPL("nil");
|
|
432
|
+
case SP_TAG_SYM: return sp_sym_inspect((sp_sym)v.v.i);
|
|
433
|
+
case SP_TAG_ENCODING: return sp_encoding_inspect_name(v.v.s ? v.v.s : "");
|
|
434
|
+
case SP_TAG_CLASS: return sp_class_val_name(v);
|
|
435
|
+
case SP_TAG_BIGINT: return sp_bigint_to_s((sp_Bigint *)v.v.p);
|
|
436
|
+
case SP_TAG_OBJ:
|
|
437
|
+
/* Built-in container / value-type tags get their typed inspect
|
|
438
|
+
helper. Matches the dispatch shape in sp_poly_to_s above and the
|
|
439
|
+
`puts` poly arm earlier in this file; without it, a Range / Time
|
|
440
|
+
/ typed Array stored as an sp_RbVal value (e.g. a sym_poly_hash
|
|
441
|
+
that mixes `200..299` and `404`) reported "#<Object>" from
|
|
442
|
+
`.inspect`, which was both wrong for CRuby parity and useless
|
|
443
|
+
for debugging. */
|
|
444
|
+
switch (v.cls_id) {
|
|
445
|
+
case SP_BUILTIN_INT_ARRAY: return sp_IntArray_inspect((sp_IntArray *)v.v.p);
|
|
446
|
+
case SP_BUILTIN_FLT_ARRAY: return sp_FloatArray_inspect((sp_FloatArray *)v.v.p);
|
|
447
|
+
case SP_BUILTIN_STR_ARRAY: return sp_StrArray_inspect((sp_StrArray *)v.v.p);
|
|
448
|
+
case SP_BUILTIN_SYM_ARRAY: return sp_SymArray_inspect((sp_IntArray *)v.v.p);
|
|
449
|
+
case SP_BUILTIN_PTR_ARRAY: return sp_PtrArray_inspect_k((sp_PtrArray *)v.v.p);
|
|
450
|
+
case SP_BUILTIN_POLY_ARRAY: return sp_PolyArray_inspect((sp_PolyArray *)v.v.p);
|
|
451
|
+
case SP_BUILTIN_RANGE: return sp_Range_inspect((sp_Range *)v.v.p);
|
|
452
|
+
case SP_BUILTIN_FLOAT_RANGE: return sp_frange_inspect(*(sp_FloatRange *)v.v.p);
|
|
453
|
+
case SP_BUILTIN_STR_RANGE: return sp_srange_inspect(*(sp_StrRange *)v.v.p);
|
|
454
|
+
case SP_BUILTIN_TIME: return sp_Time_inspect((sp_Time *)v.v.p);
|
|
455
|
+
case SP_BUILTIN_STRBUF: return sp_str_inspect(sp_String_cstr((sp_String *)v.v.p)); /* (#3227) */
|
|
456
|
+
case SP_BUILTIN_METHOD: return sp_method_desc_cstr((sp_BoundMethod *)v.v.p);
|
|
457
|
+
case SP_BUILTIN_COMPLEX: return sp_complex_inspect(*(sp_Complex *)v.v.p);
|
|
458
|
+
case SP_BUILTIN_RATIONAL: return sp_rational_inspect(*(sp_Rational *)v.v.p);
|
|
459
|
+
case SP_BUILTIN_BIG_RATIONAL: return sp_brat_inspect((sp_BigRational *)v.v.p);
|
|
460
|
+
case SP_BUILTIN_REGEX: return sp_re_inspect_str(v.v.p);
|
|
461
|
+
case SP_BUILTIN_MATCHDATA: return sp_MatchData_inspect((sp_MatchData *)v.v.p);
|
|
462
|
+
case SP_BUILTIN_EXCEPTION: return sp_exc_inspect(v.v.p);
|
|
463
|
+
case SP_BUILTIN_STR_INT_HASH: return sp_StrIntHash_inspect((sp_StrIntHash *)v.v.p);
|
|
464
|
+
case SP_BUILTIN_STR_STR_HASH: return sp_StrStrHash_inspect((sp_StrStrHash *)v.v.p);
|
|
465
|
+
case SP_BUILTIN_INT_STR_HASH: return sp_IntStrHash_inspect((sp_IntStrHash *)v.v.p);
|
|
466
|
+
case SP_BUILTIN_INT_INT_HASH: return sp_IntIntHash_inspect((sp_IntIntHash *)v.v.p);
|
|
467
|
+
case SP_BUILTIN_STR_POLY_HASH: return sp_StrPolyHash_inspect((sp_StrPolyHash *)v.v.p);
|
|
468
|
+
case SP_BUILTIN_SYM_POLY_HASH: return sp_SymPolyHash_inspect((sp_SymPolyHash *)v.v.p);
|
|
469
|
+
case SP_BUILTIN_POLY_POLY_HASH: return sp_PolyPolyHash_inspect((sp_PolyPolyHash *)v.v.p);
|
|
470
|
+
case SP_BUILTIN_OPENSTRUCT: return sp_OpenStruct_inspect((struct sp_OpenStruct_s *)v.v.p);
|
|
471
|
+
case SP_BUILTIN_ENUMERATOR: return sp_enum_inspect_boxed(v);
|
|
472
|
+
/* #<Dir:PATH>, as the typed Dir's inspect renders it (#3250) */
|
|
473
|
+
case SP_BUILTIN_DIR: {
|
|
474
|
+
const char *_dp = v.v.p ? ((sp_Dir *)v.v.p)->path : NULL;
|
|
475
|
+
return sp_sprintf("#<Dir:%s>", _dp ? _dp : "");
|
|
476
|
+
}
|
|
477
|
+
case SP_BUILTIN_FIBER: return sp_Fiber_inspect((sp_Fiber *)v.v.p);
|
|
478
|
+
case SP_BUILTIN_THREAD: return sp_Thread_inspect((sp_thread *)v.v.p);
|
|
479
|
+
case SP_BUILTIN_ARGF: return SPL("ARGF");
|
|
480
|
+
/* the typed form's rendering, which lists the ivars a program set */
|
|
481
|
+
case SP_BUILTIN_RANDOM: if (v.v.p) return sp_Random_inspect((sp_Random *)v.v.p); return SPL("#<Object>");
|
|
482
|
+
default:
|
|
483
|
+
/* a user object: the generated per-class ivar walk renders
|
|
484
|
+
#<Name:0x... @a=..., ...> like CRuby's default inspect */
|
|
485
|
+
if (v.cls_id >= 0 && sp_obj_inspect_fn && v.v.p) {
|
|
486
|
+
SP_GC_ROOT_RBVAL(v); /* across the user #inspect or the ivar walk, as for #to_s */
|
|
487
|
+
return sp_obj_inspect_fn(v.cls_id, v.v.p);
|
|
488
|
+
}
|
|
489
|
+
if (v.cls_id == SP_BUILTIN_OBJECT && v.v.p && v.v.p == sp_main_obj) return SPL("main");
|
|
490
|
+
if ((v.cls_id >= 0 || v.cls_id == SP_BUILTIN_OBJECT) && v.v.p)
|
|
491
|
+
return sp_sprintf("#<%s:0x%016llx>", sp_poly_class_name(v),
|
|
492
|
+
(unsigned long long)(uintptr_t)v.v.p);
|
|
493
|
+
/* a builtin handle with no inspect of its own (a Fiber, a Queue, a
|
|
494
|
+
Mutex): name it rather than answering the useless "#<Object>" --
|
|
495
|
+
the tag knows what it is, and CRuby's default inspect is this
|
|
496
|
+
shape too */
|
|
497
|
+
if (v.v.p) {
|
|
498
|
+
const char *bn = sp_poly_class_name(v);
|
|
499
|
+
if (bn && bn[0])
|
|
500
|
+
return sp_sprintf("#<%s:0x%016llx>", bn, (unsigned long long)(uintptr_t)v.v.p);
|
|
501
|
+
}
|
|
502
|
+
return SPL("#<Object>");
|
|
503
|
+
}
|
|
504
|
+
default: return sp_str_empty;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
sp_StrPolyHash*sp_StrPolyHash_merge(sp_StrPolyHash*a,sp_StrPolyHash*b)
|
|
509
|
+
{SP_GC_ROOT(a);SP_GC_ROOT(b);sp_StrPolyHash*r=sp_StrPolyHash_new();r->default_v=a->default_v;r->dproc=a->dproc;r->dproc_self=a->dproc_self;for(sp_int i=0;i<a->len;i++)sp_StrPolyHash_set(r,a->order[i],sp_StrPolyHash_get(a,a->order[i]));for(sp_int i=0;i<b->len;i++)sp_StrPolyHash_set(r,b->order[i],sp_StrPolyHash_get(b,b->order[i]));return r;}
|
|
510
|
+
|
|
511
|
+
sp_StrPolyHash*sp_StrPolyHash_dup(sp_StrPolyHash*h)
|
|
512
|
+
{sp_StrPolyHash*r=sp_StrPolyHash_new();r->default_v=h->default_v;r->dproc=h->dproc;r->dproc_self=h->dproc_self;for(sp_int i=0;i<h->len;i++)sp_StrPolyHash_set(r,h->order[i],sp_StrPolyHash_get(h,h->order[i]));return r;}
|
|
513
|
+
|
|
514
|
+
void sp_StrPolyHash_clear(sp_StrPolyHash*h)
|
|
515
|
+
{ sp_gc_wb((void*)h);if(!h)return;for(sp_int i=0;i<h->cap;i++)h->keys[i]=NULL;h->len=0;}
|
|
516
|
+
|
|
517
|
+
const char*sp_StrPolyHash_inspect(sp_StrPolyHash*h)
|
|
518
|
+
{return h?sp_inspect_container(sp_box_obj(h,SP_BUILTIN_STR_POLY_HASH)):SPL("nil");}
|
|
519
|
+
|
|
520
|
+
sp_StrStrHash *sp_StrPolyHash_to_s_values(sp_StrPolyHash *h)
|
|
521
|
+
{
|
|
522
|
+
SP_GC_ROOT(h);
|
|
523
|
+
sp_StrStrHash *r = sp_StrStrHash_new(); SP_GC_ROOT(r);
|
|
524
|
+
/* a missing match reads the default, as its to_s (nil's "" is no default) */
|
|
525
|
+
if (h && h->default_v.tag != SP_TAG_NIL) r->default_v = sp_poly_to_s(h->default_v);
|
|
526
|
+
if (h) for (sp_int i = 0; i < h->len; i++) {
|
|
527
|
+
const char *k = h->order[i];
|
|
528
|
+
sp_StrStrHash_set(r, k, sp_poly_to_s(sp_StrPolyHash_get(h, k)));
|
|
529
|
+
}
|
|
530
|
+
return r;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
sp_SymPolyHash*sp_SymPolyHash_merge(sp_SymPolyHash*a,sp_SymPolyHash*b)
|
|
534
|
+
{SP_GC_ROOT(a);SP_GC_ROOT(b);sp_SymPolyHash*r=sp_SymPolyHash_new();r->default_v=a->default_v;r->dproc=a->dproc;r->dproc_self=a->dproc_self;for(sp_int i=0;i<a->len;i++)sp_SymPolyHash_set(r,a->order[i],sp_SymPolyHash_get(a,a->order[i]));for(sp_int i=0;i<b->len;i++)sp_SymPolyHash_set(r,b->order[i],sp_SymPolyHash_get(b,b->order[i]));return r;}
|
|
535
|
+
|
|
536
|
+
sp_OpenStruct *sp_OpenStruct_dup(sp_OpenStruct *o, int keep_frozen)
|
|
537
|
+
{
|
|
538
|
+
if(!o) return NULL;
|
|
539
|
+
SP_GC_ROOT(o);
|
|
540
|
+
sp_SymPolyHash *t=sp_OpenStruct_to_h(o); SP_GC_ROOT(t);
|
|
541
|
+
sp_OpenStruct *r=sp_OpenStruct_new_from(t);
|
|
542
|
+
if(keep_frozen&&sp_gc_is_frozen(o)) sp_gc_freeze(r);
|
|
543
|
+
return r;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const char *sp_OpenStruct_inspect(sp_OpenStruct *o)
|
|
547
|
+
{
|
|
548
|
+
SP_GC_ROOT(o); /* o may be the caller's bare temporary; the rendering allocates */
|
|
549
|
+
/* an OpenStruct reached from inside itself renders as the ellipsis and stops,
|
|
550
|
+
as CRuby's does: `#<OpenStruct a=1, me=#<OpenStruct ...>>` */
|
|
551
|
+
if(o&&sp_poly_recur_seen(SP_POLY_RECUR_INSPECT,o,NULL)) return (&("\xff" "#<OpenStruct ...>")[1]);
|
|
552
|
+
int rmark=o?sp_poly_recur_push(SP_POLY_RECUR_INSPECT,o,NULL):-1;
|
|
553
|
+
sp_String *s=sp_String_new(""); SP_GC_ROOT(s);
|
|
554
|
+
sp_String_append(s,"#<OpenStruct");
|
|
555
|
+
if(o&&o->tbl) for(sp_int i=0;i<o->tbl->len;i++){
|
|
556
|
+
sp_sym k=o->tbl->order[i];
|
|
557
|
+
sp_String_append(s, i==0?" ":", ");
|
|
558
|
+
sp_String_append(s, sp_sym_to_s(k));
|
|
559
|
+
sp_String_append(s, "=");
|
|
560
|
+
sp_String_append(s, sp_poly_inspect(sp_SymPolyHash_get(o->tbl,k)));
|
|
561
|
+
}
|
|
562
|
+
sp_String_append(s,">");
|
|
563
|
+
if(rmark>=0) sp_poly_recur_pop(rmark);
|
|
564
|
+
/* an independent GC heap string, not the sp_String's own buffer: the wrapper
|
|
565
|
+
is unrooted on return and its data would dangle if the result is stored. */
|
|
566
|
+
return sp_str_concat(sp_String_cstr(s), (&("\xff")[1]));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
sp_SymPolyHash*sp_SymPolyHash_dup(sp_SymPolyHash*h)
|
|
570
|
+
{sp_SymPolyHash*r=sp_SymPolyHash_new();r->default_v=h->default_v;r->dproc=h->dproc;r->dproc_self=h->dproc_self;for(sp_int i=0;i<h->len;i++)sp_SymPolyHash_set(r,h->order[i],sp_SymPolyHash_get(h,h->order[i]));return r;}
|
|
571
|
+
|
|
572
|
+
void sp_SymPolyHash_clear(sp_SymPolyHash*h)
|
|
573
|
+
{if(!h)return;for(sp_int i=0;i<h->cap;i++)h->keys[i]=-1;h->len=0;}
|
|
574
|
+
|
|
575
|
+
const char*sp_SymPolyHash_inspect(sp_SymPolyHash*h)
|
|
576
|
+
{return h?sp_inspect_container(sp_box_obj(h,SP_BUILTIN_SYM_POLY_HASH)):SPL("nil");}
|
|
577
|
+
|
|
578
|
+
sp_PolyPolyHash*sp_PolyPolyHash_merge(sp_PolyPolyHash*a,sp_PolyPolyHash*b)
|
|
579
|
+
{SP_GC_ROOT(a);SP_GC_ROOT(b);sp_PolyPolyHash*r=sp_PolyPolyHash_new();SP_GC_ROOT(r);if(a){r->default_v=a->default_v;r->dproc=a->dproc;r->dproc_self=a->dproc_self;for(sp_int i=0;i<a->len;i++){sp_int idx=a->order[i];sp_PolyPolyHash_set(r,a->keys[idx],a->vals[idx]);}}if(b){for(sp_int i=0;i<b->len;i++){sp_int idx=b->order[i];sp_PolyPolyHash_set(r,b->keys[idx],b->vals[idx]);}}return r;}
|
|
580
|
+
|
|
581
|
+
void sp_PolyPolyHash_clear(sp_PolyPolyHash*h)
|
|
582
|
+
{if(!h)return;for(sp_int i=0;i<h->cap;i++)h->occ[i]=0;h->len=0;}
|
|
583
|
+
|
|
584
|
+
sp_RbVal sp_poly_clear(sp_RbVal v)
|
|
585
|
+
{
|
|
586
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == SP_BUILTIN_QUEUE && v.v.p) { sp_Queue_clear((sp_queue *)v.v.p); return v; }
|
|
587
|
+
/* String#clear on a plain string box: a fresh empty string the receiver
|
|
588
|
+
takes back, as the typed clear builds; a frozen one raises. It answered
|
|
589
|
+
the box untouched. */
|
|
590
|
+
if (v.tag == SP_TAG_STR) { sp_str_check_mutable(v.v.s); return sp_box_str(sp_str_from_bytes("", 0)); }
|
|
591
|
+
sp_poly_coll_chk(v, "clear");
|
|
592
|
+
if (v.tag != SP_TAG_OBJ || !v.v.p) return v;
|
|
593
|
+
switch (v.cls_id) {
|
|
594
|
+
case SP_BUILTIN_INT_ARRAY: ((sp_IntArray *)v.v.p)->len = 0; break;
|
|
595
|
+
case SP_BUILTIN_FLT_ARRAY: ((sp_FloatArray *)v.v.p)->len = 0; break;
|
|
596
|
+
case SP_BUILTIN_STR_ARRAY: ((sp_StrArray *)v.v.p)->len = 0; break;
|
|
597
|
+
case SP_BUILTIN_POLY_ARRAY: ((sp_PolyArray *)v.v.p)->len = 0; break;
|
|
598
|
+
case SP_BUILTIN_PTR_ARRAY: ((sp_PtrArray *)v.v.p)->len = 0; break;
|
|
599
|
+
case SP_BUILTIN_STRBUF: {
|
|
600
|
+
sp_String *_m = (sp_String *)v.v.p;
|
|
601
|
+
if (sp_String_is_frozen(_m)) { sp_raise_frozen_str(_m->data); break; }
|
|
602
|
+
_m->len = 0; _m->data[0] = 0; sp_fd_publish(_m);
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
case SP_BUILTIN_STR_INT_HASH: sp_StrIntHash_clear((sp_StrIntHash *)v.v.p); break;
|
|
606
|
+
case SP_BUILTIN_STR_STR_HASH: sp_StrStrHash_clear((sp_StrStrHash *)v.v.p); break;
|
|
607
|
+
case SP_BUILTIN_INT_STR_HASH: sp_IntStrHash_clear((sp_IntStrHash *)v.v.p); break;
|
|
608
|
+
case SP_BUILTIN_INT_INT_HASH: sp_IntIntHash_clear((sp_IntIntHash *)v.v.p); break;
|
|
609
|
+
case SP_BUILTIN_STR_POLY_HASH: sp_StrPolyHash_clear((sp_StrPolyHash *)v.v.p); break;
|
|
610
|
+
case SP_BUILTIN_SYM_POLY_HASH: sp_SymPolyHash_clear((sp_SymPolyHash *)v.v.p); break;
|
|
611
|
+
case SP_BUILTIN_POLY_POLY_HASH: sp_PolyPolyHash_clear((sp_PolyPolyHash *)v.v.p); break;
|
|
612
|
+
default: sp_raise_nomethod(sp_nomethod_msg("clear", v)); break;
|
|
613
|
+
}
|
|
614
|
+
return v;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
sp_RbVal sp_poly_slice_or_call(sp_RbVal v, sp_RbVal a, sp_RbVal b)
|
|
618
|
+
{
|
|
619
|
+
/* `s[/re/, n]` on a boxed String: capture n of the first match, nil when
|
|
620
|
+
there is none -- the form the typed emitter answers inline. Read as a
|
|
621
|
+
two-integer slice, the Regexp operand raised TypeError (campfire's
|
|
622
|
+
`response.headers["Link"][/<(.*)>/, 1]` off a Hash[String, String]). */
|
|
623
|
+
if (v.tag == SP_TAG_STR && a.tag == SP_TAG_OBJ && a.v.p && a.cls_id == SP_BUILTIN_REGEX &&
|
|
624
|
+
b.tag == SP_TAG_INT) {
|
|
625
|
+
sp_int n = b.v.i;
|
|
626
|
+
if (sp_re_match((mrb_regexp_pattern *)a.v.p, v.v.s ? v.v.s : "") < 0) return sp_box_nil();
|
|
627
|
+
if (n == 0) return sp_box_nullable_str(sp_re_match_str);
|
|
628
|
+
if (n >= 1 && n <= 9) return sp_box_nullable_str(sp_re_captures[n]);
|
|
629
|
+
return sp_box_nil();
|
|
630
|
+
}
|
|
631
|
+
/* `s[/(?<x>..)/, "x"]` or `, :x`: the named capture of the first match,
|
|
632
|
+
nil when there is none, as the typed emitter answers it */
|
|
633
|
+
if (v.tag == SP_TAG_STR && a.tag == SP_TAG_OBJ && a.v.p && a.cls_id == SP_BUILTIN_REGEX &&
|
|
634
|
+
(b.tag == SP_TAG_STR || b.tag == SP_TAG_SYM)) {
|
|
635
|
+
const char *nm = b.tag == SP_TAG_SYM ? sp_sym_to_s((sp_sym)b.v.i) : (b.v.s ? b.v.s : "");
|
|
636
|
+
if (sp_re_match((mrb_regexp_pattern *)a.v.p, v.v.s ? v.v.s : "") < 0) return sp_box_nil();
|
|
637
|
+
return sp_box_nullable_str(sp_re_named_capture((mrb_regexp_pattern *)a.v.p, nm));
|
|
638
|
+
}
|
|
639
|
+
/* a Method's [] too: its arguments of any kind are the call's, where
|
|
640
|
+
sp_poly_slice took a String for an index and raised TypeError (#6179) */
|
|
641
|
+
if (v.tag == SP_TAG_OBJ && v.v.p &&
|
|
642
|
+
(v.cls_id == SP_BUILTIN_PROC || v.cls_id == SP_BUILTIN_CURRY || v.cls_id == SP_BUILTIN_METHOD)) {
|
|
643
|
+
_sp_proc_poly_args[0] = a;
|
|
644
|
+
_sp_proc_poly_args[1] = b;
|
|
645
|
+
sp_int slots[16];
|
|
646
|
+
slots[0] = sp_poly_slot_i(a);
|
|
647
|
+
slots[1] = sp_poly_slot_i(b);
|
|
648
|
+
return sp_poly_callable_call(v, 2, slots);
|
|
649
|
+
}
|
|
650
|
+
return sp_poly_slice(v, sp_poly_arg_int_chk(a), sp_poly_arg_int_chk(b));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
sp_RbVal sp_poly_dup(sp_RbVal v, int keep_frozen)
|
|
654
|
+
{
|
|
655
|
+
if (sp_bsub_dup_hook && v.tag == SP_TAG_OBJ && v.v.p) {
|
|
656
|
+
sp_bool handled = FALSE;
|
|
657
|
+
sp_RbVal r = sp_bsub_dup_hook(v, keep_frozen, &handled);
|
|
658
|
+
if (handled) return r;
|
|
659
|
+
}
|
|
660
|
+
if (v.tag == SP_TAG_OBJ && v.v.p &&
|
|
661
|
+
(v.cls_id == SP_BUILTIN_RANGE || v.cls_id == SP_BUILTIN_FLOAT_RANGE || v.cls_id == SP_BUILTIN_STR_RANGE))
|
|
662
|
+
return sp_range_dup(v, keep_frozen);
|
|
663
|
+
|
|
664
|
+
/* Hash#dup/#clone on a boxed hash: a shallow copy of the same variant. The
|
|
665
|
+
hash went out as-is, so a `dup` taken to keep the caller's hash intact
|
|
666
|
+
aliased it and every write through the copy landed in the original
|
|
667
|
+
(#4646: a parameter typed poly by a recursive call cycle). */
|
|
668
|
+
if (v.tag == SP_TAG_OBJ && v.v.p && sp_poly_is_hash_kind(v.cls_id)) {
|
|
669
|
+
void *p = v.v.p; SP_GC_ROOT(p);
|
|
670
|
+
switch (v.cls_id) {
|
|
671
|
+
case SP_BUILTIN_STR_INT_HASH: v.v.p = sp_StrIntHash_dup((sp_StrIntHash *)p); break;
|
|
672
|
+
case SP_BUILTIN_STR_STR_HASH: v.v.p = sp_StrStrHash_dup((sp_StrStrHash *)p); break;
|
|
673
|
+
case SP_BUILTIN_INT_STR_HASH: v.v.p = sp_IntStrHash_dup((sp_IntStrHash *)p); break;
|
|
674
|
+
case SP_BUILTIN_INT_INT_HASH: v.v.p = sp_IntIntHash_dup((sp_IntIntHash *)p); break;
|
|
675
|
+
case SP_BUILTIN_STR_POLY_HASH: v.v.p = sp_StrPolyHash_dup((sp_StrPolyHash *)p); break;
|
|
676
|
+
case SP_BUILTIN_SYM_POLY_HASH: v.v.p = sp_SymPolyHash_dup((sp_SymPolyHash *)p); break;
|
|
677
|
+
case SP_BUILTIN_POLY_POLY_HASH: v.v.p = sp_PolyPolyHash_dup((sp_PolyPolyHash *)p); break;
|
|
678
|
+
}
|
|
679
|
+
if (keep_frozen && sp_gc_is_frozen(p)) sp_gc_freeze(v.v.p);
|
|
680
|
+
return v;
|
|
681
|
+
}
|
|
682
|
+
/* Array#dup/#clone on a boxed array (read out of a poly container): a shallow
|
|
683
|
+
copy of the same kind. A raw struct memcpy (the user-object path below)
|
|
684
|
+
would share the element buffer, so the copy would alias -- mutating it
|
|
685
|
+
would corrupt the original. */
|
|
686
|
+
if (v.tag == SP_TAG_OBJ && v.v.p && sp_poly_is_array_kind(v.cls_id)) {
|
|
687
|
+
switch (v.cls_id) {
|
|
688
|
+
case SP_BUILTIN_POLY_ARRAY: v.v.p = sp_PolyArray_dup((sp_PolyArray *)v.v.p); break;
|
|
689
|
+
case SP_BUILTIN_INT_ARRAY: {
|
|
690
|
+
sp_IntArray *a = (sp_IntArray *)v.v.p; SP_GC_ROOT(a);
|
|
691
|
+
sp_IntArray *r = sp_IntArray_new();
|
|
692
|
+
for (sp_int i = 0; i < a->len; i++) sp_IntArray_push(r, a->data[a->start + i]);
|
|
693
|
+
SP_MAY_NIL(r) = SP_MAY_NIL(a);
|
|
694
|
+
if (keep_frozen && a->frozen) r->frozen = 1;
|
|
695
|
+
v.v.p = r; break;
|
|
696
|
+
}
|
|
697
|
+
case SP_BUILTIN_STR_ARRAY: {
|
|
698
|
+
sp_StrArray *a = (sp_StrArray *)v.v.p; SP_GC_ROOT(a);
|
|
699
|
+
sp_StrArray *r = sp_StrArray_new();
|
|
700
|
+
for (sp_int i = 0; i < a->len; i++) sp_StrArray_push(r, a->data[i]);
|
|
701
|
+
if (keep_frozen && a->frozen) r->frozen = 1;
|
|
702
|
+
v.v.p = r; break;
|
|
703
|
+
}
|
|
704
|
+
case SP_BUILTIN_FLT_ARRAY: {
|
|
705
|
+
sp_FloatArray *a = (sp_FloatArray *)v.v.p; SP_GC_ROOT(a);
|
|
706
|
+
sp_FloatArray *r = sp_FloatArray_new();
|
|
707
|
+
for (sp_int i = 0; i < a->len; i++) sp_FloatArray_push(r, a->data[i]);
|
|
708
|
+
SP_MAY_NIL(r) = SP_MAY_NIL(a);
|
|
709
|
+
if (keep_frozen && a->frozen) r->frozen = 1;
|
|
710
|
+
v.v.p = r; break;
|
|
711
|
+
}
|
|
712
|
+
case SP_BUILTIN_PTR_ARRAY: {
|
|
713
|
+
/* a shallow copy of the same kind, its stamp carried over (#4486) */
|
|
714
|
+
sp_PtrArray *a = (sp_PtrArray *)v.v.p; SP_GC_ROOT(a);
|
|
715
|
+
sp_PtrArray *r = sp_PtrArray_new_scan(a->scan_elem);
|
|
716
|
+
r->elem_kind = a->elem_kind; r->elem_cls = a->elem_cls;
|
|
717
|
+
for (sp_int i = 0; i < a->len; i++) sp_PtrArray_push(r, a->data[i]);
|
|
718
|
+
if (keep_frozen && a->frozen) r->frozen = 1;
|
|
719
|
+
v.v.p = r; break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return v;
|
|
723
|
+
}
|
|
724
|
+
/* OpenStruct#dup/#clone on a boxed OpenStruct: a copy over its own member
|
|
725
|
+
table. Its cls_id is a builtin's, so the user-object copy below skips it
|
|
726
|
+
and the original came back. */
|
|
727
|
+
if (v.tag == SP_TAG_OBJ && v.v.p && v.cls_id == SP_BUILTIN_OPENSTRUCT) {
|
|
728
|
+
v.v.p = sp_OpenStruct_dup((sp_OpenStruct *)v.v.p, keep_frozen);
|
|
729
|
+
return v;
|
|
730
|
+
}
|
|
731
|
+
/* String#dup on a boxed string: a copy, unfrozen unless kept. Handed back
|
|
732
|
+
as-is, a `(fmt || FORMAT).dup` aliased the frozen constant and the next
|
|
733
|
+
gsub! on the "copy" raised FrozenError (Benchmark::Tms#format). */
|
|
734
|
+
if (v.tag == SP_TAG_STR && v.v.s) {
|
|
735
|
+
const char *src = v.v.s;
|
|
736
|
+
const char *d = sp_str_dup(src);
|
|
737
|
+
if (keep_frozen && sp_str_is_frozen_val(src)) d = sp_str_freeze_val(d);
|
|
738
|
+
v.v.s = d;
|
|
739
|
+
return v;
|
|
740
|
+
}
|
|
741
|
+
/* String#dup/#clone on a boxed shared handle (#6179): a new handle over a
|
|
742
|
+
copy of the bytes, its ASCII-8BIT tag kept and its frozen flag only for
|
|
743
|
+
clone. Its cls_id is a builtin's, so the user-object copy below skipped
|
|
744
|
+
it and the handle itself came back: an append to the "copy" landed in
|
|
745
|
+
the original, and `clone(freeze: false)` of a frozen String stayed
|
|
746
|
+
frozen. */
|
|
747
|
+
if (v.tag == SP_TAG_OBJ && v.v.p && v.cls_id == SP_BUILTIN_STRBUF) {
|
|
748
|
+
sp_String *h = (sp_String *)v.v.p; SP_GC_ROOT(h);
|
|
749
|
+
sp_String *n = sp_String_new_unfrozen(sp_String_cstr(h));
|
|
750
|
+
if (keep_frozen && sp_String_is_frozen(h)) sp_String_freeze(n);
|
|
751
|
+
v.v.p = n;
|
|
752
|
+
return v;
|
|
753
|
+
}
|
|
754
|
+
if (v.tag == SP_TAG_OBJ && v.v.p &&
|
|
755
|
+
(v.cls_id >= 0 || v.cls_id == SP_BUILTIN_OBJECT)) {
|
|
756
|
+
sp_gc_hdr *h = (sp_gc_hdr *)((char *)v.v.p - sizeof(sp_gc_hdr));
|
|
757
|
+
size_t payload = h->size - sizeof(sp_gc_hdr);
|
|
758
|
+
void *src = v.v.p;
|
|
759
|
+
SP_GC_ROOT(src);
|
|
760
|
+
void *n = sp_gc_alloc(payload, h->finalize, h->scan);
|
|
761
|
+
memcpy(n, src, payload);
|
|
762
|
+
/* a bare Object's ivars are its own table: the copy takes a copy */
|
|
763
|
+
if (v.cls_id == SP_BUILTIN_OBJECT && ((sp_Object *)n)->ivars) {
|
|
764
|
+
SP_GC_ROOT(n);
|
|
765
|
+
sp_SymPolyHash *t = sp_SymPolyHash_dup(((sp_Object *)n)->ivars);
|
|
766
|
+
((sp_Object *)n)->ivars = t;
|
|
767
|
+
}
|
|
768
|
+
if (sp_user_init_copy_hook) { SP_GC_ROOT(n); sp_RbVal r = v; r.v.p = n; sp_user_init_copy_hook(r, v); }
|
|
769
|
+
if (keep_frozen && h->frozen)
|
|
770
|
+
((sp_gc_hdr *)((char *)n - sizeof(sp_gc_hdr)))->frozen = 1;
|
|
771
|
+
v.v.p = n;
|
|
772
|
+
}
|
|
773
|
+
return v;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
void sp_poly_hash_merge_into(sp_RbVal dst, sp_RbVal src)
|
|
777
|
+
{
|
|
778
|
+
if (dst.tag != SP_TAG_OBJ || !sp_poly_is_hash_kind(dst.cls_id)) return;
|
|
779
|
+
if (src.tag != SP_TAG_OBJ || !sp_poly_is_hash_kind(src.cls_id)) return;
|
|
780
|
+
sp_int n = sp_poly_length(src);
|
|
781
|
+
for (sp_int i = 0; i < n; i++) {
|
|
782
|
+
sp_RbVal k, v;
|
|
783
|
+
sp_poly_hash_pair(src, i, &k, &v);
|
|
784
|
+
switch (dst.cls_id) {
|
|
785
|
+
case SP_BUILTIN_POLY_POLY_HASH: sp_PolyPolyHash_set((sp_PolyPolyHash *)dst.v.p, k, v); break;
|
|
786
|
+
case SP_BUILTIN_SYM_POLY_HASH:
|
|
787
|
+
if (k.tag == SP_TAG_SYM) sp_SymPolyHash_set((sp_SymPolyHash *)dst.v.p, (sp_sym)k.v.i, v);
|
|
788
|
+
break;
|
|
789
|
+
case SP_BUILTIN_STR_POLY_HASH:
|
|
790
|
+
if (k.tag == SP_TAG_STR) sp_StrPolyHash_set((sp_StrPolyHash *)dst.v.p, k.v.s, v);
|
|
791
|
+
break;
|
|
792
|
+
case SP_BUILTIN_STR_STR_HASH:
|
|
793
|
+
if (k.tag == SP_TAG_STR && v.tag == SP_TAG_STR)
|
|
794
|
+
sp_StrStrHash_set((sp_StrStrHash *)dst.v.p, k.v.s, v.v.s);
|
|
795
|
+
break;
|
|
796
|
+
case SP_BUILTIN_STR_INT_HASH:
|
|
797
|
+
if (k.tag == SP_TAG_STR && v.tag == SP_TAG_INT)
|
|
798
|
+
sp_StrIntHash_set((sp_StrIntHash *)dst.v.p, k.v.s, v.v.i);
|
|
799
|
+
break;
|
|
800
|
+
case SP_BUILTIN_INT_INT_HASH:
|
|
801
|
+
if (k.tag == SP_TAG_INT && v.tag == SP_TAG_INT)
|
|
802
|
+
sp_IntIntHash_set((sp_IntIntHash *)dst.v.p, k.v.i, v.v.i);
|
|
803
|
+
break;
|
|
804
|
+
case SP_BUILTIN_INT_STR_HASH:
|
|
805
|
+
if (k.tag == SP_TAG_INT && v.tag == SP_TAG_STR)
|
|
806
|
+
sp_IntStrHash_set((sp_IntStrHash *)dst.v.p, k.v.i, v.v.s);
|
|
807
|
+
break;
|
|
808
|
+
default: break;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
sp_RbVal sp_kw_splat_check(sp_RbVal h, const char *const *mem, int n, const unsigned char *lit, int is_data)
|
|
814
|
+
{
|
|
815
|
+
SP_GC_ROOT_RBVAL(h);
|
|
816
|
+
char buf[1024]; size_t len = 0; int cnt = 0;
|
|
817
|
+
buf[0] = 0;
|
|
818
|
+
sp_int nk = sp_poly_length(h);
|
|
819
|
+
if (is_data) {
|
|
820
|
+
for (sp_int j = 0; j < nk; j++) {
|
|
821
|
+
sp_RbVal k, v;
|
|
822
|
+
sp_poly_hash_pair(h, j, &k, &v);
|
|
823
|
+
sp_poly_to_name(k);
|
|
824
|
+
}
|
|
825
|
+
for (int i = 0; i < n; i++) {
|
|
826
|
+
if (lit && lit[i]) continue;
|
|
827
|
+
int f = 0;
|
|
828
|
+
for (sp_int j = 0; j < nk && !f; j++) {
|
|
829
|
+
sp_RbVal k, v;
|
|
830
|
+
sp_poly_hash_pair(h, j, &k, &v);
|
|
831
|
+
f = strcmp(sp_kw_key_name(k), mem[i]) == 0;
|
|
832
|
+
}
|
|
833
|
+
if (f) continue;
|
|
834
|
+
if (len < sizeof buf) len += (size_t)snprintf(buf + len, sizeof buf - len, "%s:%s", cnt ? ", " : "", mem[i]);
|
|
835
|
+
cnt++;
|
|
836
|
+
}
|
|
837
|
+
if (cnt) sp_raise_kw_error("missing", cnt, buf);
|
|
838
|
+
}
|
|
839
|
+
sp_PolyPolyHash *named = NULL;
|
|
840
|
+
SP_GC_ROOT(named);
|
|
841
|
+
for (sp_int j = 0; j < nk && !is_data && !named; j++) {
|
|
842
|
+
sp_RbVal k, v;
|
|
843
|
+
sp_poly_hash_pair(h, j, &k, &v);
|
|
844
|
+
if (!sp_kw_key_name(k)) named = sp_PolyPolyHash_new();
|
|
845
|
+
}
|
|
846
|
+
for (sp_int j = 0; j < nk; j++) {
|
|
847
|
+
sp_RbVal k, v;
|
|
848
|
+
sp_poly_hash_pair(h, j, &k, &v);
|
|
849
|
+
const char *kn = sp_kw_key_name(k);
|
|
850
|
+
int at = -1;
|
|
851
|
+
sp_int idx = 0;
|
|
852
|
+
for (int i = 0; kn && i < n && at < 0; i++) if (strcmp(kn, mem[i]) == 0) at = i;
|
|
853
|
+
if (!kn) at = sp_kw_key_pos(k, n, &idx);
|
|
854
|
+
if (at >= 0) {
|
|
855
|
+
if (named) sp_PolyPolyHash_set(named, sp_box_sym(sp_sym_intern(mem[at])), v);
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
if (len < sizeof buf) len += (size_t)snprintf(buf + len, sizeof buf - len, "%s%s", cnt ? ", " : "",
|
|
859
|
+
is_data ? sp_poly_inspect(k) : kn ? kn : sp_int_to_s(idx));
|
|
860
|
+
cnt++;
|
|
861
|
+
}
|
|
862
|
+
/* a Struct's own wording names its keys bare and always plural
|
|
863
|
+
(rb_struct_initialize_m) */
|
|
864
|
+
if (cnt && is_data) sp_raise_kw_error("unknown", cnt, buf);
|
|
865
|
+
if (cnt) sp_raise_cls("ArgumentError", sp_sprintf("unknown keywords: %s", buf));
|
|
866
|
+
return named ? sp_box_obj(named, SP_BUILTIN_POLY_POLY_HASH) : h;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
sp_RbVal sp_poly_to_h_m(sp_RbVal v)
|
|
870
|
+
{
|
|
871
|
+
if (v.tag == SP_TAG_NIL) return sp_box_obj(sp_SymPolyHash_new(), SP_BUILTIN_SYM_POLY_HASH);
|
|
872
|
+
if (v.tag == SP_TAG_OBJ && sp_poly_is_hash_kind(v.cls_id)) return v;
|
|
873
|
+
/* an OpenStruct (an OpenStruct|nil union reaches here boxed): its member
|
|
874
|
+
table is already a symbol-keyed hash (#3282) */
|
|
875
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == SP_BUILTIN_OPENSTRUCT)
|
|
876
|
+
return sp_box_obj(sp_OpenStruct_to_h((sp_OpenStruct *)v.v.p), SP_BUILTIN_SYM_POLY_HASH);
|
|
877
|
+
/* a Struct/Data read out of a container: dispatch its symbol-keyed to_h by
|
|
878
|
+
cls_id through the generated hook (#2906). */
|
|
879
|
+
if (v.tag == SP_TAG_OBJ && sp_obj_to_h_fn) {
|
|
880
|
+
sp_RbVal h = sp_obj_to_h_fn(v);
|
|
881
|
+
if (h.tag == SP_TAG_OBJ) return h;
|
|
882
|
+
}
|
|
883
|
+
/* an array of [k, v] pairs -> a hash keyed by whatever the pairs hold: a
|
|
884
|
+
Symbol-keyed one where every key is a Symbol (the Hash#partition
|
|
885
|
+
sub-array and Enumerable pair lists this was written for), the general
|
|
886
|
+
boxed hash otherwise -- reading an Integer key as a symbol id built a
|
|
887
|
+
hash whose keys were other programs' symbols (#3972) */
|
|
888
|
+
if (v.tag == SP_TAG_OBJ && sp_poly_is_array_kind(v.cls_id)) {
|
|
889
|
+
sp_int n = sp_poly_length(v);
|
|
890
|
+
int all_sym = 1;
|
|
891
|
+
for (sp_int i = 0; i < n && all_sym; i++) {
|
|
892
|
+
sp_RbVal pair = sp_poly_arr_get(v, i);
|
|
893
|
+
/* CRuby's messages, as sp_poly_to_h_val raises them */
|
|
894
|
+
if (!(pair.tag == SP_TAG_OBJ && sp_poly_is_array_kind(pair.cls_id)))
|
|
895
|
+
sp_raise_cls("TypeError", sp_sprintf("wrong element type %s at %lld (expected array)",
|
|
896
|
+
sp_poly_class_name(pair), (long long)i));
|
|
897
|
+
if (sp_poly_length(pair) != 2)
|
|
898
|
+
sp_raise_cls("ArgumentError", sp_sprintf("wrong array length at %lld (expected 2, was %lld)",
|
|
899
|
+
(long long)i, (long long)sp_poly_length(pair)));
|
|
900
|
+
if (sp_poly_arr_get(pair, 0).tag != SP_TAG_SYM) all_sym = 0;
|
|
901
|
+
}
|
|
902
|
+
if (!all_sym) {
|
|
903
|
+
sp_PolyPolyHash *ph = sp_PolyPolyHash_new();
|
|
904
|
+
SP_GC_ROOT(ph);
|
|
905
|
+
for (sp_int i = 0; i < n; i++) {
|
|
906
|
+
sp_RbVal pair = sp_poly_arr_get(v, i);
|
|
907
|
+
sp_PolyPolyHash_set(ph, sp_poly_arr_get(pair, 0), sp_poly_arr_get(pair, 1));
|
|
908
|
+
}
|
|
909
|
+
return sp_box_obj(ph, SP_BUILTIN_POLY_POLY_HASH);
|
|
910
|
+
}
|
|
911
|
+
sp_SymPolyHash *h = sp_SymPolyHash_new();
|
|
912
|
+
SP_GC_ROOT(h);
|
|
913
|
+
for (sp_int i = 0; i < n; i++) {
|
|
914
|
+
sp_RbVal pair = sp_poly_arr_get(v, i);
|
|
915
|
+
sp_RbVal k = sp_poly_arr_get(pair, 0);
|
|
916
|
+
sp_SymPolyHash_set(h, (sp_sym)k.v.i, sp_poly_arr_get(pair, 1));
|
|
917
|
+
}
|
|
918
|
+
return sp_box_obj(h, SP_BUILTIN_SYM_POLY_HASH);
|
|
919
|
+
}
|
|
920
|
+
/* an Integer Range has Enumerable#to_h: its elements are no pairs, so a
|
|
921
|
+
non-empty one is CRuby's TypeError, and an empty one is {} */
|
|
922
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == SP_BUILTIN_RANGE && v.v.p) {
|
|
923
|
+
if (sp_range_count(*(sp_Range *)v.v.p) <= 0) return sp_box_obj(sp_SymPolyHash_new(), SP_BUILTIN_SYM_POLY_HASH);
|
|
924
|
+
sp_raise_cls("TypeError", "wrong element type Integer (expected array)");
|
|
925
|
+
}
|
|
926
|
+
/* a String Range likewise, its elements Strings */
|
|
927
|
+
if (v.tag == SP_TAG_OBJ && v.cls_id == SP_BUILTIN_STR_RANGE && v.v.p) {
|
|
928
|
+
if (!sp_srange_min_v(*(sp_StrRange *)v.v.p)) return sp_box_obj(sp_SymPolyHash_new(), SP_BUILTIN_SYM_POLY_HASH);
|
|
929
|
+
sp_raise_cls("TypeError", "wrong element type String (expected array)");
|
|
930
|
+
}
|
|
931
|
+
sp_raise_nomethod(sp_nomethod_msg("to_h", v)); /* "for true", "for an instance of String", as CRuby words it */
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
void sp_kwargs_verify_at(sp_RbVal h, const char *const *allowed, const char *const *required, const char *const *lit, const char *const *unk, int nbefore, int check_unknown)
|
|
935
|
+
{
|
|
936
|
+
sp_PolyArray *k = sp_poly_length(h) > 0 ? sp_poly_keys(h) : sp_PolyArray_new(); SP_GC_ROOT(k);
|
|
937
|
+
char list[256]; int n = 0, cnt = 0;
|
|
938
|
+
list[0] = 0;
|
|
939
|
+
for (const char *const *r = required; *r; r++) {
|
|
940
|
+
int found = sp_kwargs_name_in(*r, lit);
|
|
941
|
+
for (sp_int i = 0; i < k->len && !found; i++)
|
|
942
|
+
if (k->data[i].tag == SP_TAG_SYM && !strcmp(sp_sym_to_s((sp_sym)k->data[i].v.i), *r)) found = 1;
|
|
943
|
+
if (!found) sp_kwargs_list_add(list, &n, &cnt, sp_sprintf(":%s", *r));
|
|
944
|
+
}
|
|
945
|
+
if (cnt) sp_raise_kw_error("missing", cnt, list);
|
|
946
|
+
if (!check_unknown) return;
|
|
947
|
+
int nunk = 0;
|
|
948
|
+
for (const char *const *u = unk; u && *u; u++) nunk++;
|
|
949
|
+
int nb = nbefore < 0 || nbefore > nunk ? nunk : nbefore;
|
|
950
|
+
for (int u = 0; u < nb; u++) sp_kwargs_list_add(list, &n, &cnt, unk[u]);
|
|
951
|
+
for (const char *const *l = lit; !unk && *l; l++)
|
|
952
|
+
if (!sp_kwargs_name_in(*l, allowed)) sp_kwargs_list_add(list, &n, &cnt, sp_sprintf(":%s", *l));
|
|
953
|
+
for (sp_int i = 0; i < k->len; i++) {
|
|
954
|
+
const char *iv = sp_poly_inspect(k->data[i]);
|
|
955
|
+
int later = 0; /* a key written after the `**` too: named here, where the `**` holds it */
|
|
956
|
+
for (int u = nb; u < nunk && !later; u++) later = !strcmp(iv, unk[u]);
|
|
957
|
+
if (k->data[i].tag == SP_TAG_SYM) {
|
|
958
|
+
const char *nm = sp_sym_to_s((sp_sym)k->data[i].v.i);
|
|
959
|
+
if (sp_kwargs_name_in(nm, allowed) || (!later && sp_kwargs_name_in(nm, lit))) continue;
|
|
960
|
+
}
|
|
961
|
+
int ahead = 0; /* a literal key's ahead of the `**`, named already */
|
|
962
|
+
for (int u = 0; u < nb && !ahead; u++) ahead = !strcmp(iv, unk[u]);
|
|
963
|
+
if (ahead) continue;
|
|
964
|
+
sp_kwargs_list_add(list, &n, &cnt, iv);
|
|
965
|
+
}
|
|
966
|
+
for (int u = nb; u < nunk; u++) {
|
|
967
|
+
int in_hash = 0;
|
|
968
|
+
for (sp_int i = 0; i < k->len && !in_hash; i++) in_hash = !strcmp(sp_poly_inspect(k->data[i]), unk[u]);
|
|
969
|
+
if (!in_hash) sp_kwargs_list_add(list, &n, &cnt, unk[u]);
|
|
970
|
+
}
|
|
971
|
+
if (cnt) sp_raise_kw_error("unknown", cnt, list);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
void sp_kwrest_merge_poly(sp_SymPolyHash *dst, sp_RbVal h)
|
|
975
|
+
{
|
|
976
|
+
SP_GC_ROOT(dst);
|
|
977
|
+
h = sp_kw_splat_conv(h, 0);
|
|
978
|
+
if (h.tag == SP_TAG_NIL) return;
|
|
979
|
+
SP_GC_ROOT_RBVAL(h);
|
|
980
|
+
sp_int n = sp_poly_length(h);
|
|
981
|
+
for (sp_int i = 0; i < n; i++) {
|
|
982
|
+
sp_RbVal k, v;
|
|
983
|
+
sp_poly_hash_pair(h, i, &k, &v);
|
|
984
|
+
if (k.tag != SP_TAG_SYM) sp_poly_typed_hash_store_miss(k, v, "Symbol", NULL);
|
|
985
|
+
sp_SymPolyHash_set(dst, (sp_sym)k.v.i, v);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
void sp_kw_merge_any(sp_PolyPolyHash *dst, sp_RbVal h)
|
|
990
|
+
{
|
|
991
|
+
SP_GC_ROOT(dst);
|
|
992
|
+
h = sp_kw_splat_conv(h, 0);
|
|
993
|
+
if (h.tag == SP_TAG_NIL) return;
|
|
994
|
+
SP_GC_ROOT_RBVAL(h);
|
|
995
|
+
sp_int n = sp_poly_length(h);
|
|
996
|
+
for (sp_int i = 0; i < n; i++) {
|
|
997
|
+
sp_RbVal k, v;
|
|
998
|
+
sp_poly_hash_pair(h, i, &k, &v);
|
|
999
|
+
sp_PolyPolyHash_set(dst, k, v);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
sp_PolyPolyHash*sp_PolyPolyHash_dup(sp_PolyPolyHash*h)
|
|
1004
|
+
{SP_GC_ROOT(h);sp_PolyPolyHash*r=sp_PolyPolyHash_new();SP_GC_ROOT(r);r->default_v=h->default_v;r->dproc=h->dproc;r->dproc_self=h->dproc_self;for(sp_int i=0;i<h->len;i++)sp_PolyPolyHash_set(r,h->keys[h->order[i]],h->vals[h->order[i]]);return r;}
|
|
1005
|
+
|
|
1006
|
+
const char*sp_PolyPolyHash_inspect(sp_PolyPolyHash*h)
|
|
1007
|
+
{return h?sp_inspect_container(sp_box_obj(h,SP_BUILTIN_POLY_POLY_HASH)):SPL("nil");}
|
|
1008
|
+
|
|
1009
|
+
sp_PolyPolyHash *sp_poly_hash_merge(sp_RbVal a, sp_RbVal b)
|
|
1010
|
+
{
|
|
1011
|
+
/* Before the allocation, not after: the operands are read for the whole
|
|
1012
|
+
loop below and the new hash is the first thing that can collect them.
|
|
1013
|
+
Copying them into hs[] does not root them -- the collector walks the
|
|
1014
|
+
registered root slots, not the stack. */
|
|
1015
|
+
SP_GC_ROOT_RBVAL(a); SP_GC_ROOT_RBVAL(b);
|
|
1016
|
+
sp_PolyPolyHash *r = sp_PolyPolyHash_new();
|
|
1017
|
+
SP_GC_ROOT(r);
|
|
1018
|
+
/* merge inherits the receiver's default; cross-layout receivers arrive boxed */
|
|
1019
|
+
if (a.tag == SP_TAG_OBJ && a.v.p) {
|
|
1020
|
+
switch (a.cls_id) {
|
|
1021
|
+
case SP_BUILTIN_STR_INT_HASH: r->default_v = sp_box_int_or_nil(((sp_StrIntHash *)a.v.p)->default_v); break;
|
|
1022
|
+
case SP_BUILTIN_STR_STR_HASH: r->default_v = sp_box_nullable_str(((sp_StrStrHash *)a.v.p)->default_v); break;
|
|
1023
|
+
case SP_BUILTIN_INT_STR_HASH: r->default_v = sp_box_nullable_str(((sp_IntStrHash *)a.v.p)->default_v); break;
|
|
1024
|
+
case SP_BUILTIN_INT_INT_HASH: r->default_v = sp_box_int_or_nil(((sp_IntIntHash *)a.v.p)->default_v); break;
|
|
1025
|
+
case SP_BUILTIN_STR_POLY_HASH: r->default_v = ((sp_StrPolyHash *)a.v.p)->default_v; break;
|
|
1026
|
+
case SP_BUILTIN_SYM_POLY_HASH: r->default_v = ((sp_SymPolyHash *)a.v.p)->default_v; break;
|
|
1027
|
+
case SP_BUILTIN_POLY_POLY_HASH: r->default_v = ((sp_PolyPolyHash *)a.v.p)->default_v; break;
|
|
1028
|
+
default: break;
|
|
1029
|
+
}
|
|
1030
|
+
int has_dproc = 0;
|
|
1031
|
+
switch (a.cls_id) {
|
|
1032
|
+
case SP_BUILTIN_STR_POLY_HASH: has_dproc = ((sp_StrPolyHash *)a.v.p)->dproc != NULL; break;
|
|
1033
|
+
case SP_BUILTIN_SYM_POLY_HASH: has_dproc = ((sp_SymPolyHash *)a.v.p)->dproc != NULL; break;
|
|
1034
|
+
case SP_BUILTIN_POLY_POLY_HASH: has_dproc = ((sp_PolyPolyHash *)a.v.p)->dproc != NULL; break;
|
|
1035
|
+
default: break;
|
|
1036
|
+
}
|
|
1037
|
+
if (has_dproc) {
|
|
1038
|
+
sp_poly_hash_dproc_ctx *ctx = (sp_poly_hash_dproc_ctx *)sp_gc_alloc(
|
|
1039
|
+
sizeof(*ctx), NULL, sp_poly_hash_dproc_ctx_scan);
|
|
1040
|
+
ctx->source = a;
|
|
1041
|
+
r->dproc = sp_poly_hash_dproc_bridge;
|
|
1042
|
+
r->dproc_self = ctx;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
sp_RbVal hs[2]; hs[0] = a; hs[1] = b;
|
|
1046
|
+
for (int h = 0; h < 2; h++) {
|
|
1047
|
+
if (hs[h].tag != SP_TAG_OBJ || !sp_poly_is_hash_kind(hs[h].cls_id)) continue;
|
|
1048
|
+
sp_PolyArray *pairs = sp_poly_to_a_arr(hs[h]);
|
|
1049
|
+
SP_GC_ROOT(pairs);
|
|
1050
|
+
for (sp_int i = 0; pairs && i < pairs->len; i++) {
|
|
1051
|
+
sp_RbVal pair = pairs->data[i];
|
|
1052
|
+
sp_PolyPolyHash_set(r, sp_poly_arr_get(pair, 0), sp_poly_arr_get(pair, 1));
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return r;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
sp_RbVal sp_poly_hash_slice(sp_RbVal v, int n, sp_RbVal *keys)
|
|
1059
|
+
{
|
|
1060
|
+
/* The receiver is read once per key while the loop below allocates the
|
|
1061
|
+
result and grows it, and it is a temporary whenever the call chains off
|
|
1062
|
+
one (`poly(1).slice(*keys)`). Unrooted, it was collected part way through
|
|
1063
|
+
and the remaining keys missed. */
|
|
1064
|
+
SP_GC_ROOT_RBVAL(v);
|
|
1065
|
+
sp_PolyPolyHash *h = sp_PolyPolyHash_new();
|
|
1066
|
+
SP_GC_ROOT(h);
|
|
1067
|
+
for (int i = 0; i < n; i++)
|
|
1068
|
+
if (sp_poly_has_key(v, keys[i]))
|
|
1069
|
+
sp_PolyPolyHash_set(h, keys[i], sp_poly_index_poly(v, keys[i]));
|
|
1070
|
+
return sp_box_obj(h, SP_BUILTIN_POLY_POLY_HASH);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
void sp_poly_hash_writeback_ex(sp_RbVal orig, sp_PolyPolyHash *work, int with_default)
|
|
1074
|
+
{
|
|
1075
|
+
if (orig.tag != SP_TAG_OBJ || !work || !orig.v.p) return;
|
|
1076
|
+
if (orig.cls_id == SP_BUILTIN_POLY_POLY_HASH) return;
|
|
1077
|
+
if (!sp_poly_is_hash_kind(orig.cls_id)) return;
|
|
1078
|
+
if (sp_gc_is_frozen(orig.v.p)) sp_raise_frozen_hash_at(orig.v.p, orig.cls_id);
|
|
1079
|
+
SP_GC_ROOT_RBVAL(orig); SP_GC_ROOT(work);
|
|
1080
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1081
|
+
sp_int j = work->order[i];
|
|
1082
|
+
sp_RbVal k = work->keys[j], v = work->vals[j];
|
|
1083
|
+
switch (orig.cls_id) {
|
|
1084
|
+
case SP_BUILTIN_STR_INT_HASH: sp_hash_wb_want(k, SP_TAG_STR, 0, "key", "String keys"); sp_hash_wb_want(v, SP_TAG_INT, 1, "value", "Integer values"); break;
|
|
1085
|
+
case SP_BUILTIN_STR_STR_HASH: sp_hash_wb_want(k, SP_TAG_STR, 0, "key", "String keys"); sp_hash_wb_want(v, SP_TAG_STR, 1, "value", "String values"); break;
|
|
1086
|
+
case SP_BUILTIN_INT_STR_HASH: sp_hash_wb_want(k, SP_TAG_INT, 0, "key", "Integer keys"); sp_hash_wb_want(v, SP_TAG_STR, 1, "value", "String values"); break;
|
|
1087
|
+
case SP_BUILTIN_INT_INT_HASH: sp_hash_wb_want(k, SP_TAG_INT, 0, "key", "Integer keys"); sp_hash_wb_want(v, SP_TAG_INT, 1, "value", "Integer values"); break;
|
|
1088
|
+
case SP_BUILTIN_STR_POLY_HASH: sp_hash_wb_want(k, SP_TAG_STR, 0, "key", "String keys"); break;
|
|
1089
|
+
case SP_BUILTIN_SYM_POLY_HASH: sp_hash_wb_want(k, SP_TAG_SYM, 0, "key", "Symbol keys"); break;
|
|
1090
|
+
default: return;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
/* The default too, when the mutator may have set one (`with_default`: a
|
|
1094
|
+
boxed `h.default = v` set it on the copy; a `replace` keeps the
|
|
1095
|
+
receiver's own, as the typed emitter does). An Integer- or
|
|
1096
|
+
String-valued variant holds one of its own kind (or nil); the Symbol-
|
|
1097
|
+
and String-keyed poly variants hold any value. A default PROC set
|
|
1098
|
+
through the box (a PolyPolyHash body) has no slot of its signature in
|
|
1099
|
+
a typed variant: refused, like a key of the wrong kind, rather than
|
|
1100
|
+
dropped. The bridge is the original's own proc, installed on the copy
|
|
1101
|
+
by sp_poly_hash_merge, and means no change. */
|
|
1102
|
+
if (with_default) {
|
|
1103
|
+
if (work->dproc && work->dproc != sp_poly_hash_dproc_bridge)
|
|
1104
|
+
sp_raise_cls("TypeError", sp_sprintf("can't store a default proc in a %s through a boxed receiver",
|
|
1105
|
+
sp_poly_class_name(orig)));
|
|
1106
|
+
switch (orig.cls_id) {
|
|
1107
|
+
case SP_BUILTIN_STR_INT_HASH: case SP_BUILTIN_INT_INT_HASH: sp_hash_wb_want(work->default_v, SP_TAG_INT, 1, "default", "Integer values"); break;
|
|
1108
|
+
case SP_BUILTIN_STR_STR_HASH: case SP_BUILTIN_INT_STR_HASH: sp_hash_wb_want(work->default_v, SP_TAG_STR, 1, "default", "String values"); break;
|
|
1109
|
+
default: break;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
switch (orig.cls_id) {
|
|
1113
|
+
case SP_BUILTIN_STR_INT_HASH: {
|
|
1114
|
+
sp_StrIntHash *h = (sp_StrIntHash *)orig.v.p;
|
|
1115
|
+
sp_StrIntHash_clear(h);
|
|
1116
|
+
if (with_default) h->default_v = sp_poly_to_i_or_nil(work->default_v);
|
|
1117
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1118
|
+
sp_int j = work->order[i];
|
|
1119
|
+
sp_StrIntHash_set(h, sp_poly_to_s(work->keys[j]), sp_poly_to_i_or_nil(work->vals[j]));
|
|
1120
|
+
}
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
case SP_BUILTIN_STR_STR_HASH: {
|
|
1124
|
+
sp_StrStrHash *h = (sp_StrStrHash *)orig.v.p;
|
|
1125
|
+
sp_StrStrHash_clear(h);
|
|
1126
|
+
if (with_default) h->default_v = sp_poly_to_s_or_nil(work->default_v);
|
|
1127
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1128
|
+
sp_int j = work->order[i];
|
|
1129
|
+
sp_StrStrHash_set(h, sp_poly_to_s(work->keys[j]), sp_poly_to_s_or_nil(work->vals[j]));
|
|
1130
|
+
}
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
case SP_BUILTIN_INT_STR_HASH: {
|
|
1134
|
+
sp_IntStrHash *h = (sp_IntStrHash *)orig.v.p;
|
|
1135
|
+
sp_IntStrHash_clear(h);
|
|
1136
|
+
if (with_default) h->default_v = sp_poly_to_s_or_nil(work->default_v);
|
|
1137
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1138
|
+
sp_int j = work->order[i];
|
|
1139
|
+
sp_IntStrHash_set(h, sp_poly_to_i(work->keys[j]), sp_poly_to_s_or_nil(work->vals[j]));
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
case SP_BUILTIN_INT_INT_HASH: {
|
|
1144
|
+
sp_IntIntHash *h = (sp_IntIntHash *)orig.v.p;
|
|
1145
|
+
sp_IntIntHash_clear(h);
|
|
1146
|
+
if (with_default) h->default_v = sp_poly_to_i_or_nil(work->default_v);
|
|
1147
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1148
|
+
sp_int j = work->order[i];
|
|
1149
|
+
sp_IntIntHash_set(h, sp_poly_to_i(work->keys[j]), sp_poly_to_i_or_nil(work->vals[j]));
|
|
1150
|
+
}
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
case SP_BUILTIN_STR_POLY_HASH: {
|
|
1154
|
+
sp_StrPolyHash *h = (sp_StrPolyHash *)orig.v.p;
|
|
1155
|
+
sp_StrPolyHash_clear(h);
|
|
1156
|
+
if (with_default) h->default_v = work->default_v;
|
|
1157
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1158
|
+
sp_int j = work->order[i];
|
|
1159
|
+
sp_StrPolyHash_set(h, sp_poly_to_s(work->keys[j]), work->vals[j]);
|
|
1160
|
+
}
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
case SP_BUILTIN_SYM_POLY_HASH: {
|
|
1164
|
+
sp_SymPolyHash *h = (sp_SymPolyHash *)orig.v.p;
|
|
1165
|
+
sp_SymPolyHash_clear(h);
|
|
1166
|
+
if (with_default) h->default_v = work->default_v;
|
|
1167
|
+
for (sp_int i = 0; i < work->len; i++) {
|
|
1168
|
+
sp_int j = work->order[i];
|
|
1169
|
+
sp_SymPolyHash_set(h, (sp_sym)work->keys[j].v.i, work->vals[j]);
|
|
1170
|
+
}
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
default: return;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
void sp_poly_hash_writeback(sp_RbVal orig, sp_PolyPolyHash *work)
|
|
1178
|
+
{
|
|
1179
|
+
sp_poly_hash_writeback_ex(orig, work, 1);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
sp_RbVal sp_builtin_class_new(int kind, sp_int argc, const sp_RbVal *av, sp_Proc *blk)
|
|
1183
|
+
{
|
|
1184
|
+
SP_GC_ROOT(blk);
|
|
1185
|
+
switch (kind) {
|
|
1186
|
+
case 'S': {
|
|
1187
|
+
sp_dyn_new_arity(argc, 1);
|
|
1188
|
+
if (argc == 0) return sp_box_str(sp_str_dup_external((&("\xff")[1])));
|
|
1189
|
+
sp_RbVal s = sp_poly_is_strbuf(av[0]) ? sp_poly_strbuf_deref(av[0]) : av[0];
|
|
1190
|
+
if (s.tag != SP_TAG_STR)
|
|
1191
|
+
sp_raise_cls("TypeError", sp_sprintf("no implicit conversion of %s into String", sp_poly_class_name(s)));
|
|
1192
|
+
return sp_box_str(sp_str_dup(s.v.s));
|
|
1193
|
+
}
|
|
1194
|
+
case 'A': {
|
|
1195
|
+
sp_dyn_new_arity(argc, 2);
|
|
1196
|
+
sp_PolyArray *r = sp_PolyArray_new(); SP_GC_ROOT(r);
|
|
1197
|
+
if (argc == 0) return sp_box_poly_array(r);
|
|
1198
|
+
if (argc == 1 && av[0].tag == SP_TAG_OBJ && sp_poly_is_array_kind(av[0].cls_id)) {
|
|
1199
|
+
sp_int n = sp_poly_length(av[0]);
|
|
1200
|
+
for (sp_int i = 0; i < n; i++) sp_PolyArray_push(r, sp_poly_arr_get(av[0], i));
|
|
1201
|
+
return sp_box_poly_array(r);
|
|
1202
|
+
}
|
|
1203
|
+
if (av[0].tag != SP_TAG_INT)
|
|
1204
|
+
sp_raise_cls("TypeError", sp_sprintf("no implicit conversion of %s into Integer", sp_poly_class_name(av[0])));
|
|
1205
|
+
if (av[0].v.i < 0) sp_raise_cls("ArgumentError", "negative array size");
|
|
1206
|
+
for (sp_int i = 0; i < av[0].v.i; i++)
|
|
1207
|
+
sp_PolyArray_push(r, blk ? sp_penum_call1(blk, sp_box_int(i)) : argc == 2 ? av[1] : sp_box_nil());
|
|
1208
|
+
return sp_box_poly_array(r);
|
|
1209
|
+
}
|
|
1210
|
+
case 'H': {
|
|
1211
|
+
sp_dyn_new_arity(argc, blk ? 0 : 1);
|
|
1212
|
+
sp_PolyPolyHash *h = blk ? sp_PolyPolyHash_new_dproc(sp_dyn_hash_dproc, blk)
|
|
1213
|
+
: argc ? sp_PolyPolyHash_new_with_default(av[0]) : sp_PolyPolyHash_new();
|
|
1214
|
+
return sp_box_obj(h, SP_BUILTIN_POLY_POLY_HASH);
|
|
1215
|
+
}
|
|
1216
|
+
default:
|
|
1217
|
+
sp_dyn_new_arity(argc, 0);
|
|
1218
|
+
return sp_box_obj(sp_Object_new(), SP_BUILTIN_OBJECT);
|
|
1219
|
+
}
|
|
1220
|
+
}
|