json 2.19.4 → 2.19.9
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 +4 -4
- data/CHANGES.md +27 -1
- data/README.md +11 -0
- data/ext/json/ext/fbuffer/fbuffer.h +31 -23
- data/ext/json/ext/generator/generator.c +37 -22
- data/ext/json/ext/parser/parser.c +76 -28
- data/lib/json/truffle_ruby/generator.rb +3 -0
- data/lib/json/version.rb +1 -1
- metadata +65 -63
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e5fed66aaa650ac7aaf223c4d8e505a2d6ce3fe72599af4931356ae619f020b
|
|
4
|
+
data.tar.gz: 347004780c7a7568685502ade79f145371a49a213d348a65c99ea8fbffc6eee7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0bffdb4cd21e4656a1402a00d95ae890dc75befff379aeef4aad575a7f715a0dbeb87cfef6fefa3cd5235c3302a29fabb510d5db67e527678023cd8a88c34b49
|
|
7
|
+
data.tar.gz: 7e08cfae0c5d404687644528ecfb9438c9ea254f43fd285d116ba1542a4ef78045463b18850324e91ba4d0c735e7d9ed4b583499aadafe7d0bab51dd54a8d086
|
data/CHANGES.md
CHANGED
|
@@ -2,9 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-06-11 (2.19.9)
|
|
6
|
+
|
|
7
|
+
* Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
|
|
8
|
+
with `JSON.generate(object, io)`. [CVE-PENDING].
|
|
9
|
+
|
|
10
|
+
### 2026-06-03 (2.19.8)
|
|
11
|
+
|
|
12
|
+
* Fix 1-byte buffer overread on EOS errors.
|
|
13
|
+
* Handle invalid types passed as `max_nesting` option.
|
|
14
|
+
|
|
15
|
+
### 2026-05-28 (2.19.7)
|
|
16
|
+
|
|
17
|
+
* Fix some more edge cases with out of range floats.
|
|
18
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
19
|
+
* Add missing write barriers in `State#dup`.
|
|
20
|
+
* Further validate generator `depth` config.
|
|
21
|
+
|
|
22
|
+
### 2026-05-28 (2.19.6)
|
|
23
|
+
|
|
24
|
+
* Cleanly handle overly large `depth` generator argument.
|
|
25
|
+
* Add missing write barrier in `ParserConfig`.
|
|
26
|
+
|
|
27
|
+
### 2026-05-04 (2.19.5)
|
|
28
|
+
|
|
29
|
+
* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
|
|
30
|
+
|
|
5
31
|
### 2026-04-19 (2.19.4)
|
|
6
32
|
|
|
7
|
-
* Fix parsing of out of range floats (very large exponents that lead
|
|
33
|
+
* Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
|
|
8
34
|
|
|
9
35
|
### 2026-03-25 (2.19.3)
|
|
10
36
|
|
data/README.md
CHANGED
|
@@ -249,6 +249,17 @@ There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
|
|
|
249
249
|
`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
|
|
250
250
|
the `pp` library's `pp` methods.
|
|
251
251
|
|
|
252
|
+
## Security
|
|
253
|
+
|
|
254
|
+
When parsing or serializing untrusted input, parser and generator options should never be user controlled.
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# Dangerous, DO NOT DO THIS.
|
|
258
|
+
JSON.generate(params[:data], params[:options])
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
|
|
262
|
+
|
|
252
263
|
## Development
|
|
253
264
|
|
|
254
265
|
### Prerequisites
|
|
@@ -37,13 +37,17 @@ static void fbuffer_append_long(FBuffer *fb, long number);
|
|
|
37
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
38
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
39
39
|
|
|
40
|
-
static void
|
|
40
|
+
static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (RTEST(io)) {
|
|
43
|
+
JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
|
|
44
|
+
fb->io = io;
|
|
45
|
+
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
|
|
46
|
+
} else {
|
|
44
47
|
fb->type = FBUFFER_STACK_ALLOCATED;
|
|
45
48
|
fb->ptr = stack_buffer;
|
|
46
49
|
fb->capa = stack_buffer_size;
|
|
50
|
+
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
|
47
51
|
}
|
|
48
52
|
#if JSON_DEBUG
|
|
49
53
|
fb->requested = 0;
|
|
@@ -79,45 +83,40 @@ static void fbuffer_flush(FBuffer *fb)
|
|
|
79
83
|
fbuffer_clear(fb);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
static void fbuffer_realloc(FBuffer *fb, size_t
|
|
86
|
+
static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
|
|
83
87
|
{
|
|
84
|
-
if (
|
|
88
|
+
if (new_capa > fb->capa) {
|
|
85
89
|
if (fb->type == FBUFFER_STACK_ALLOCATED) {
|
|
86
90
|
const char *old_buffer = fb->ptr;
|
|
87
|
-
fb->ptr = ALLOC_N(char,
|
|
91
|
+
fb->ptr = ALLOC_N(char, new_capa);
|
|
88
92
|
fb->type = FBUFFER_HEAP_ALLOCATED;
|
|
89
93
|
MEMCPY(fb->ptr, old_buffer, char, fb->len);
|
|
90
94
|
} else {
|
|
91
|
-
REALLOC_N(fb->ptr, char,
|
|
95
|
+
REALLOC_N(fb->ptr, char, new_capa);
|
|
92
96
|
}
|
|
93
|
-
fb->capa =
|
|
97
|
+
fb->capa = new_capa;
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
|
|
98
102
|
{
|
|
99
103
|
if (RB_UNLIKELY(fb->io)) {
|
|
100
|
-
if (fb->capa
|
|
101
|
-
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
|
|
102
|
-
} else {
|
|
104
|
+
if (fb->capa != 0) {
|
|
103
105
|
fbuffer_flush(fb);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return;
|
|
106
|
+
if (RB_LIKELY(requested < fb->capa)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
size_t
|
|
112
|
+
size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
|
|
113
|
+
size_t needed_capa = requested + fb->len;
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
fb->capa = fb->initial_length;
|
|
115
|
+
while (new_capa < needed_capa) {
|
|
116
|
+
new_capa *= 2;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
fbuffer_realloc(fb, required);
|
|
119
|
+
fbuffer_realloc(fb, new_capa);
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
@@ -131,6 +130,15 @@ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
133
|
+
static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
|
|
134
|
+
{
|
|
135
|
+
size_t result = a * b;
|
|
136
|
+
if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
|
|
137
|
+
rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
135
143
|
{
|
|
136
144
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
@@ -175,7 +183,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
|
175
183
|
size_t len;
|
|
176
184
|
RSTRING_GETMEM(str, ptr, len);
|
|
177
185
|
|
|
178
|
-
fbuffer_inc_capa(fb, repeat
|
|
186
|
+
fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
|
|
179
187
|
while (repeat) {
|
|
180
188
|
#if JSON_DEBUG
|
|
181
189
|
fb->requested = len;
|
|
@@ -819,9 +819,17 @@ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
|
819
819
|
return Qundef;
|
|
820
820
|
}
|
|
821
821
|
|
|
822
|
+
static inline int json_str_coderange(VALUE str) {
|
|
823
|
+
int coderange = RB_ENC_CODERANGE(str);
|
|
824
|
+
if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
|
|
825
|
+
coderange = rb_enc_str_coderange(str);
|
|
826
|
+
}
|
|
827
|
+
return coderange;
|
|
828
|
+
}
|
|
829
|
+
|
|
822
830
|
static inline bool valid_json_string_p(VALUE str)
|
|
823
831
|
{
|
|
824
|
-
int coderange =
|
|
832
|
+
int coderange = json_str_coderange(str);
|
|
825
833
|
|
|
826
834
|
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
827
835
|
return true;
|
|
@@ -834,12 +842,8 @@ static inline bool valid_json_string_p(VALUE str)
|
|
|
834
842
|
return false;
|
|
835
843
|
}
|
|
836
844
|
|
|
837
|
-
static
|
|
845
|
+
NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
838
846
|
{
|
|
839
|
-
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
840
|
-
return str;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
847
|
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
844
848
|
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
845
849
|
if (coerced_str != str) {
|
|
@@ -875,6 +879,16 @@ static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE
|
|
|
875
879
|
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
876
880
|
}
|
|
877
881
|
|
|
882
|
+
ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
883
|
+
{
|
|
884
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
885
|
+
return str;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
return convert_invalid_encoding(data, str, as_json_called, is_key);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
878
892
|
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
879
893
|
{
|
|
880
894
|
fbuffer_append_char(buffer, '"');
|
|
@@ -893,7 +907,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
|
|
|
893
907
|
search.chunk_end = NULL;
|
|
894
908
|
#endif /* HAVE_SIMD */
|
|
895
909
|
|
|
896
|
-
switch (
|
|
910
|
+
switch (json_str_coderange(obj)) {
|
|
897
911
|
case ENC_CODERANGE_7BIT:
|
|
898
912
|
case ENC_CODERANGE_VALID:
|
|
899
913
|
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
@@ -1290,10 +1304,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun
|
|
|
1290
1304
|
GET_STATE(self);
|
|
1291
1305
|
|
|
1292
1306
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1293
|
-
FBuffer buffer = {
|
|
1294
|
-
|
|
1295
|
-
};
|
|
1296
|
-
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1307
|
+
FBuffer buffer = { 0 };
|
|
1308
|
+
fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1297
1309
|
|
|
1298
1310
|
struct generate_json_data data = {
|
|
1299
1311
|
.buffer = &buffer,
|
|
@@ -1353,12 +1365,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1353
1365
|
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1354
1366
|
|
|
1355
1367
|
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1368
|
+
|
|
1369
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
|
|
1370
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space);
|
|
1371
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
|
|
1372
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
|
|
1373
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
|
|
1374
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
|
|
1375
|
+
|
|
1362
1376
|
return obj;
|
|
1363
1377
|
}
|
|
1364
1378
|
|
|
@@ -1565,7 +1579,7 @@ static VALUE cState_max_nesting(VALUE self)
|
|
|
1565
1579
|
|
|
1566
1580
|
static long long_config(VALUE num)
|
|
1567
1581
|
{
|
|
1568
|
-
return RTEST(num) ?
|
|
1582
|
+
return RTEST(num) ? NUM2LONG(num) : 0;
|
|
1569
1583
|
}
|
|
1570
1584
|
|
|
1571
1585
|
// depth must never be negative; reject early with a clear error.
|
|
@@ -1576,6 +1590,9 @@ static long depth_config(VALUE num)
|
|
|
1576
1590
|
if (RB_UNLIKELY(d < 0)) {
|
|
1577
1591
|
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1578
1592
|
}
|
|
1593
|
+
if (RB_UNLIKELY(d > INT_MAX)) {
|
|
1594
|
+
rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
|
|
1595
|
+
}
|
|
1579
1596
|
return d;
|
|
1580
1597
|
}
|
|
1581
1598
|
|
|
@@ -1847,10 +1864,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io,
|
|
|
1847
1864
|
configure_state(&state, Qfalse, opts);
|
|
1848
1865
|
|
|
1849
1866
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1850
|
-
FBuffer buffer = {
|
|
1851
|
-
|
|
1852
|
-
};
|
|
1853
|
-
fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1867
|
+
FBuffer buffer = { 0 };
|
|
1868
|
+
fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1854
1869
|
|
|
1855
1870
|
struct generate_json_data data = {
|
|
1856
1871
|
.buffer = &buffer,
|
|
@@ -364,6 +364,7 @@ typedef struct JSON_ParserStateStruct {
|
|
|
364
364
|
rvalue_cache name_cache;
|
|
365
365
|
int in_array;
|
|
366
366
|
int current_nesting;
|
|
367
|
+
unsigned int emitted_deprecations;
|
|
367
368
|
} JSON_ParserState;
|
|
368
369
|
|
|
369
370
|
static inline size_t rest(JSON_ParserState *state) {
|
|
@@ -384,6 +385,13 @@ static inline char peek(JSON_ParserState *state)
|
|
|
384
385
|
|
|
385
386
|
static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out)
|
|
386
387
|
{
|
|
388
|
+
JSON_ASSERT(state->cursor <= state->end);
|
|
389
|
+
|
|
390
|
+
// Redundant but helpful for hardening
|
|
391
|
+
if (RB_UNLIKELY(state->cursor > state->end)) {
|
|
392
|
+
state->cursor = state->end;
|
|
393
|
+
}
|
|
394
|
+
|
|
387
395
|
const char *cursor = state->cursor;
|
|
388
396
|
long column = 0;
|
|
389
397
|
long line = 1;
|
|
@@ -945,7 +953,12 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
|
|
|
945
953
|
case JSON_IGNORE:
|
|
946
954
|
break;
|
|
947
955
|
case JSON_DEPRECATED:
|
|
948
|
-
|
|
956
|
+
// Only emit the first few deprecations to avoid spamming.
|
|
957
|
+
if (state->emitted_deprecations < 5) {
|
|
958
|
+
emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
|
|
959
|
+
state->emitted_deprecations++;
|
|
960
|
+
}
|
|
961
|
+
|
|
949
962
|
break;
|
|
950
963
|
case JSON_RAISE:
|
|
951
964
|
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
|
@@ -1016,6 +1029,13 @@ ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
|
|
|
1016
1029
|
}
|
|
1017
1030
|
state->cursor++;
|
|
1018
1031
|
}
|
|
1032
|
+
|
|
1033
|
+
// If the string ended with an unterminated escape sequence, we might
|
|
1034
|
+
// have gone past the end.
|
|
1035
|
+
if (RB_UNLIKELY(state->cursor > state->end)) {
|
|
1036
|
+
state->cursor = state->end;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1019
1039
|
return false;
|
|
1020
1040
|
}
|
|
1021
1041
|
|
|
@@ -1196,7 +1216,11 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
|
|
|
1196
1216
|
raise_parse_error_at("invalid number: %s", state, start);
|
|
1197
1217
|
}
|
|
1198
1218
|
|
|
1199
|
-
|
|
1219
|
+
if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
|
|
1220
|
+
exponent = negative_exponent ? INT64_MIN : INT64_MAX;
|
|
1221
|
+
} else {
|
|
1222
|
+
exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
|
|
1223
|
+
}
|
|
1200
1224
|
}
|
|
1201
1225
|
|
|
1202
1226
|
if (integer) {
|
|
@@ -1451,23 +1475,39 @@ static void json_ensure_eof(JSON_ParserState *state)
|
|
|
1451
1475
|
|
|
1452
1476
|
static VALUE convert_encoding(VALUE source)
|
|
1453
1477
|
{
|
|
1454
|
-
|
|
1478
|
+
StringValue(source);
|
|
1479
|
+
int encindex = RB_ENCODING_GET(source);
|
|
1455
1480
|
|
|
1456
|
-
|
|
1481
|
+
if (RB_LIKELY(encindex == utf8_encindex)) {
|
|
1482
|
+
return source;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if (encindex == binary_encindex) {
|
|
1486
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8
|
|
1487
|
+
return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
|
|
1491
|
+
StringValue(source);
|
|
1457
1492
|
return source;
|
|
1458
|
-
|
|
1493
|
+
}
|
|
1459
1494
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1495
|
+
struct parser_config_init_args {
|
|
1496
|
+
JSON_ParserConfig *config;
|
|
1497
|
+
VALUE self;
|
|
1498
|
+
};
|
|
1464
1499
|
|
|
1465
|
-
|
|
1500
|
+
static void parser_config_wb_write(VALUE self, VALUE *dest, VALUE val)
|
|
1501
|
+
{
|
|
1502
|
+
*dest = val;
|
|
1503
|
+
if (self) RB_OBJ_WRITTEN(self, Qundef, val);
|
|
1466
1504
|
}
|
|
1467
1505
|
|
|
1468
1506
|
static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
1469
1507
|
{
|
|
1470
|
-
|
|
1508
|
+
struct parser_config_init_args *args = (struct parser_config_init_args *)data;
|
|
1509
|
+
JSON_ParserConfig *config = args->config;
|
|
1510
|
+
VALUE self = args->self;
|
|
1471
1511
|
|
|
1472
1512
|
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
1473
1513
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
@@ -1476,15 +1516,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1476
1516
|
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
1477
1517
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1478
1518
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1479
|
-
else if (key == sym_on_load) { config->on_load_proc
|
|
1519
|
+
else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
|
|
1480
1520
|
else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1481
1521
|
else if (key == sym_decimal_class) {
|
|
1482
1522
|
if (RTEST(val)) {
|
|
1483
1523
|
if (rb_respond_to(val, i_try_convert)) {
|
|
1484
|
-
config->decimal_class
|
|
1524
|
+
parser_config_wb_write(self, &config->decimal_class, val);
|
|
1485
1525
|
config->decimal_method_id = i_try_convert;
|
|
1486
1526
|
} else if (rb_respond_to(val, i_new)) {
|
|
1487
|
-
config->decimal_class
|
|
1527
|
+
parser_config_wb_write(self, &config->decimal_class, val);
|
|
1488
1528
|
config->decimal_method_id = i_new;
|
|
1489
1529
|
} else if (RB_TYPE_P(val, T_CLASS)) {
|
|
1490
1530
|
VALUE name = rb_class_name(val);
|
|
@@ -1493,7 +1533,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1493
1533
|
if (last_colon) {
|
|
1494
1534
|
const char *mod_path_end = last_colon - 1;
|
|
1495
1535
|
VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
|
|
1496
|
-
config->decimal_class
|
|
1536
|
+
parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
|
|
1497
1537
|
|
|
1498
1538
|
const char *method_name_beg = last_colon + 1;
|
|
1499
1539
|
long before_len = method_name_beg - name_cstr;
|
|
@@ -1501,7 +1541,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1501
1541
|
VALUE method_name = rb_str_substr(name, before_len, len);
|
|
1502
1542
|
config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
|
|
1503
1543
|
} else {
|
|
1504
|
-
config->decimal_class
|
|
1544
|
+
parser_config_wb_write(self, &config->decimal_class, rb_mKernel);
|
|
1505
1545
|
config->decimal_method_id = SYM2ID(rb_str_intern(name));
|
|
1506
1546
|
}
|
|
1507
1547
|
}
|
|
@@ -1511,16 +1551,21 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1511
1551
|
return ST_CONTINUE;
|
|
1512
1552
|
}
|
|
1513
1553
|
|
|
1514
|
-
static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
1554
|
+
static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self)
|
|
1515
1555
|
{
|
|
1516
1556
|
config->max_nesting = 100;
|
|
1517
1557
|
|
|
1558
|
+
struct parser_config_init_args args = {
|
|
1559
|
+
.config = config,
|
|
1560
|
+
.self = self,
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1518
1563
|
if (!NIL_P(opts)) {
|
|
1519
1564
|
Check_Type(opts, T_HASH);
|
|
1520
1565
|
if (RHASH_SIZE(opts) > 0) {
|
|
1521
1566
|
// We assume in most cases few keys are set so it's faster to go over
|
|
1522
1567
|
// the provided keys than to check all possible keys.
|
|
1523
|
-
rb_hash_foreach(opts, parser_config_init_i, (VALUE)
|
|
1568
|
+
rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
|
|
1524
1569
|
}
|
|
1525
1570
|
|
|
1526
1571
|
}
|
|
@@ -1554,17 +1599,21 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
|
1554
1599
|
rb_check_frozen(self);
|
|
1555
1600
|
GET_PARSER_CONFIG;
|
|
1556
1601
|
|
|
1557
|
-
parser_config_init(config, opts);
|
|
1558
|
-
|
|
1559
|
-
RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
|
|
1602
|
+
parser_config_init(config, opts, self);
|
|
1560
1603
|
|
|
1561
1604
|
return self;
|
|
1562
1605
|
}
|
|
1563
1606
|
|
|
1564
|
-
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE
|
|
1607
|
+
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
1565
1608
|
{
|
|
1566
|
-
Vsource = convert_encoding(
|
|
1567
|
-
|
|
1609
|
+
VALUE Vsource = convert_encoding(src);
|
|
1610
|
+
|
|
1611
|
+
// Ensure the string isn't mutated under us.
|
|
1612
|
+
// The classic API to use is `rb_str_locktmp`, but then we'd
|
|
1613
|
+
// need to use `rb_protect` to make sure we always unlock.
|
|
1614
|
+
if (Vsource == src) {
|
|
1615
|
+
Vsource = rb_str_new_frozen(Vsource);
|
|
1616
|
+
}
|
|
1568
1617
|
|
|
1569
1618
|
VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
|
|
1570
1619
|
rvalue_stack stack = {
|
|
@@ -1575,6 +1624,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1575
1624
|
|
|
1576
1625
|
long len;
|
|
1577
1626
|
const char *start;
|
|
1627
|
+
|
|
1578
1628
|
RSTRING_GETMEM(Vsource, start, len);
|
|
1579
1629
|
|
|
1580
1630
|
VALUE stack_handle = 0;
|
|
@@ -1593,6 +1643,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1593
1643
|
// it won't cause a leak.
|
|
1594
1644
|
rvalue_stack_eagerly_release(stack_handle);
|
|
1595
1645
|
RB_GC_GUARD(stack_handle);
|
|
1646
|
+
RB_GC_GUARD(Vsource);
|
|
1596
1647
|
json_ensure_eof(state);
|
|
1597
1648
|
|
|
1598
1649
|
return result;
|
|
@@ -1613,12 +1664,9 @@ static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
|
|
|
1613
1664
|
|
|
1614
1665
|
static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
|
|
1615
1666
|
{
|
|
1616
|
-
Vsource = convert_encoding(StringValue(Vsource));
|
|
1617
|
-
StringValue(Vsource);
|
|
1618
|
-
|
|
1619
1667
|
JSON_ParserConfig _config = {0};
|
|
1620
1668
|
JSON_ParserConfig *config = &_config;
|
|
1621
|
-
parser_config_init(config, opts);
|
|
1669
|
+
parser_config_init(config, opts, false);
|
|
1622
1670
|
|
|
1623
1671
|
return cParser_parse(config, Vsource);
|
|
1624
1672
|
}
|
|
@@ -307,6 +307,9 @@ module JSON
|
|
|
307
307
|
if !opts.key?(:max_nesting) # defaults to 100
|
|
308
308
|
@max_nesting = 100
|
|
309
309
|
elsif opts[:max_nesting]
|
|
310
|
+
unless opts[:max_nesting].is_a?(Integer)
|
|
311
|
+
raise TypeError, ":max_nesting must be an Integer, got: #{opts[:max_nesting].class}"
|
|
312
|
+
end
|
|
310
313
|
@max_nesting = opts[:max_nesting]
|
|
311
314
|
else
|
|
312
315
|
@max_nesting = 0
|
data/lib/json/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.19.
|
|
4
|
+
version: 2.19.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- Florian Frank
|
|
7
|
+
- Florian Frank
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -13,78 +13,80 @@ description: This is a JSON implementation as a Ruby extension in C.
|
|
|
13
13
|
email: flori@ping.de
|
|
14
14
|
executables: []
|
|
15
15
|
extensions:
|
|
16
|
-
- ext/json/ext/generator/extconf.rb
|
|
17
|
-
- ext/json/ext/parser/extconf.rb
|
|
16
|
+
- ext/json/ext/generator/extconf.rb
|
|
17
|
+
- ext/json/ext/parser/extconf.rb
|
|
18
18
|
extra_rdoc_files:
|
|
19
|
-
- README.md
|
|
19
|
+
- README.md
|
|
20
20
|
files:
|
|
21
|
-
- BSDL
|
|
22
|
-
- CHANGES.md
|
|
23
|
-
- COPYING
|
|
24
|
-
- LEGAL
|
|
25
|
-
- README.md
|
|
26
|
-
- ext/json/ext/fbuffer/fbuffer.h
|
|
27
|
-
- ext/json/ext/generator/extconf.rb
|
|
28
|
-
- ext/json/ext/generator/generator.c
|
|
29
|
-
- ext/json/ext/json.h
|
|
30
|
-
- ext/json/ext/parser/extconf.rb
|
|
31
|
-
- ext/json/ext/parser/parser.c
|
|
32
|
-
- ext/json/ext/simd/conf.rb
|
|
33
|
-
- ext/json/ext/simd/simd.h
|
|
34
|
-
- ext/json/ext/vendor/fpconv.c
|
|
35
|
-
- ext/json/ext/vendor/jeaiii-ltoa.h
|
|
36
|
-
- ext/json/ext/vendor/ryu.h
|
|
37
|
-
- json.gemspec
|
|
38
|
-
- lib/json.rb
|
|
39
|
-
- lib/json/add/bigdecimal.rb
|
|
40
|
-
- lib/json/add/complex.rb
|
|
41
|
-
- lib/json/add/core.rb
|
|
42
|
-
- lib/json/add/date.rb
|
|
43
|
-
- lib/json/add/date_time.rb
|
|
44
|
-
- lib/json/add/exception.rb
|
|
45
|
-
- lib/json/add/ostruct.rb
|
|
46
|
-
- lib/json/add/range.rb
|
|
47
|
-
- lib/json/add/rational.rb
|
|
48
|
-
- lib/json/add/regexp.rb
|
|
49
|
-
- lib/json/add/set.rb
|
|
50
|
-
- lib/json/add/string.rb
|
|
51
|
-
- lib/json/add/struct.rb
|
|
52
|
-
- lib/json/add/symbol.rb
|
|
53
|
-
- lib/json/add/time.rb
|
|
54
|
-
- lib/json/common.rb
|
|
55
|
-
- lib/json/ext.rb
|
|
56
|
-
- lib/json/ext/generator/state.rb
|
|
57
|
-
- lib/json/generic_object.rb
|
|
58
|
-
- lib/json/truffle_ruby/generator.rb
|
|
59
|
-
- lib/json/version.rb
|
|
60
|
-
homepage: https://github.com/ruby/json
|
|
21
|
+
- BSDL
|
|
22
|
+
- CHANGES.md
|
|
23
|
+
- COPYING
|
|
24
|
+
- LEGAL
|
|
25
|
+
- README.md
|
|
26
|
+
- ext/json/ext/fbuffer/fbuffer.h
|
|
27
|
+
- ext/json/ext/generator/extconf.rb
|
|
28
|
+
- ext/json/ext/generator/generator.c
|
|
29
|
+
- ext/json/ext/json.h
|
|
30
|
+
- ext/json/ext/parser/extconf.rb
|
|
31
|
+
- ext/json/ext/parser/parser.c
|
|
32
|
+
- ext/json/ext/simd/conf.rb
|
|
33
|
+
- ext/json/ext/simd/simd.h
|
|
34
|
+
- ext/json/ext/vendor/fpconv.c
|
|
35
|
+
- ext/json/ext/vendor/jeaiii-ltoa.h
|
|
36
|
+
- ext/json/ext/vendor/ryu.h
|
|
37
|
+
- json.gemspec
|
|
38
|
+
- lib/json.rb
|
|
39
|
+
- lib/json/add/bigdecimal.rb
|
|
40
|
+
- lib/json/add/complex.rb
|
|
41
|
+
- lib/json/add/core.rb
|
|
42
|
+
- lib/json/add/date.rb
|
|
43
|
+
- lib/json/add/date_time.rb
|
|
44
|
+
- lib/json/add/exception.rb
|
|
45
|
+
- lib/json/add/ostruct.rb
|
|
46
|
+
- lib/json/add/range.rb
|
|
47
|
+
- lib/json/add/rational.rb
|
|
48
|
+
- lib/json/add/regexp.rb
|
|
49
|
+
- lib/json/add/set.rb
|
|
50
|
+
- lib/json/add/string.rb
|
|
51
|
+
- lib/json/add/struct.rb
|
|
52
|
+
- lib/json/add/symbol.rb
|
|
53
|
+
- lib/json/add/time.rb
|
|
54
|
+
- lib/json/common.rb
|
|
55
|
+
- lib/json/ext.rb
|
|
56
|
+
- lib/json/ext/generator/state.rb
|
|
57
|
+
- lib/json/generic_object.rb
|
|
58
|
+
- lib/json/truffle_ruby/generator.rb
|
|
59
|
+
- lib/json/version.rb
|
|
60
|
+
homepage: "https://github.com/ruby/json"
|
|
61
61
|
licenses:
|
|
62
|
-
- Ruby
|
|
62
|
+
- Ruby
|
|
63
63
|
metadata:
|
|
64
|
-
bug_tracker_uri: https://github.com/ruby/json/issues
|
|
65
|
-
changelog_uri: https://github.com/ruby/json/blob/master/CHANGES.md
|
|
66
|
-
documentation_uri: https://docs.ruby-lang.org/en/master/JSON.html
|
|
67
|
-
homepage_uri: https://github.com/ruby/json
|
|
68
|
-
source_code_uri: https://github.com/ruby/json
|
|
64
|
+
bug_tracker_uri: "https://github.com/ruby/json/issues"
|
|
65
|
+
changelog_uri: "https://github.com/ruby/json/blob/master/CHANGES.md"
|
|
66
|
+
documentation_uri: "https://docs.ruby-lang.org/en/master/JSON.html"
|
|
67
|
+
homepage_uri: "https://github.com/ruby/json"
|
|
68
|
+
source_code_uri: "https://github.com/ruby/json"
|
|
69
69
|
rdoc_options:
|
|
70
|
-
- "--title"
|
|
71
|
-
- JSON implementation for Ruby
|
|
72
|
-
- "--main"
|
|
73
|
-
- README.md
|
|
70
|
+
- "--title"
|
|
71
|
+
- JSON implementation for Ruby
|
|
72
|
+
- "--main"
|
|
73
|
+
- README.md
|
|
74
74
|
require_paths:
|
|
75
|
-
- lib
|
|
75
|
+
- lib
|
|
76
76
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
77
|
requirements:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
-
|
|
79
|
+
- ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: "2.7"
|
|
81
82
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
83
|
requirements:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
-
|
|
85
|
+
- ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: "0"
|
|
86
88
|
requirements: []
|
|
87
|
-
rubygems_version: 4.0.
|
|
89
|
+
rubygems_version: 4.1.0.dev
|
|
88
90
|
specification_version: 4
|
|
89
91
|
summary: JSON Implementation for Ruby
|
|
90
92
|
test_files: []
|