json 2.21.2 → 3.0.0
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 +47 -1
- data/README.md +4 -84
- data/ext/json/ext/generator/generator.c +32 -38
- data/ext/json/ext/json.h +13 -0
- data/ext/json/ext/parser/parser.c +80 -84
- data/lib/json/common.rb +136 -403
- data/lib/json/ext/generator/state.rb +0 -27
- data/lib/json/ext.rb +0 -2
- data/lib/json/truffle_ruby/generator.rb +38 -80
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +13 -265
- metadata +1 -17
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -13
- data/lib/json/add/date.rb +0 -54
- data/lib/json/add/date_time.rb +0 -67
- data/lib/json/add/exception.rb +0 -49
- data/lib/json/add/ostruct.rb +0 -54
- data/lib/json/add/range.rb +0 -54
- data/lib/json/add/rational.rb +0 -49
- data/lib/json/add/regexp.rb +0 -48
- data/lib/json/add/set.rb +0 -48
- data/lib/json/add/string.rb +0 -35
- data/lib/json/add/struct.rb +0 -52
- data/lib/json/add/symbol.rb +0 -52
- data/lib/json/add/time.rb +0 -52
- data/lib/json/generic_object.rb +0 -67
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
static VALUE mJSON, eNestingError, eParserError, Encoding_UTF_8;
|
|
6
6
|
static VALUE CNaN, CInfinity, CMinusInfinity, JSON_empty_string;
|
|
7
7
|
|
|
8
|
-
static ID i_new, i_try_convert, i_encode, i_at_line, i_at_column;
|
|
8
|
+
static ID i_new, i_try_convert, i_encode, i_at_line, i_at_column, i_at_json_path;
|
|
9
9
|
#ifndef HAVE_RB_STR_TO_INTERNED_STR
|
|
10
10
|
static ID i_uminus;
|
|
11
11
|
#endif
|
|
@@ -403,19 +403,13 @@ typedef struct json_frame_stack_struct {
|
|
|
403
403
|
json_frame *ptr;
|
|
404
404
|
} json_frame_stack;
|
|
405
405
|
|
|
406
|
-
enum deprecatable_action {
|
|
407
|
-
JSON_DEPRECATED = 0,
|
|
408
|
-
JSON_IGNORE,
|
|
409
|
-
JSON_RAISE,
|
|
410
|
-
};
|
|
411
|
-
|
|
412
406
|
typedef struct JSON_ParserStruct {
|
|
413
407
|
VALUE on_load_proc;
|
|
414
408
|
VALUE decimal_class;
|
|
415
409
|
ID decimal_method_id;
|
|
416
|
-
enum deprecatable_action on_duplicate_key;
|
|
417
|
-
enum deprecatable_action on_comment;
|
|
418
410
|
int max_nesting;
|
|
411
|
+
bool allow_comments;
|
|
412
|
+
bool allow_duplicate_key;
|
|
419
413
|
bool allow_nan;
|
|
420
414
|
bool allow_trailing_comma;
|
|
421
415
|
bool allow_control_characters;
|
|
@@ -435,7 +429,6 @@ typedef struct JSON_ParserStateStruct {
|
|
|
435
429
|
rvalue_cache name_cache;
|
|
436
430
|
int in_array;
|
|
437
431
|
int current_nesting;
|
|
438
|
-
unsigned int emitted_deprecations;
|
|
439
432
|
VALUE parser;
|
|
440
433
|
} JSON_ParserState;
|
|
441
434
|
|
|
@@ -619,21 +612,6 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum
|
|
|
619
612
|
*column_out = column;
|
|
620
613
|
}
|
|
621
614
|
|
|
622
|
-
static const unsigned int MAX_DEPRECATIONS = 5;
|
|
623
|
-
|
|
624
|
-
static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
625
|
-
{
|
|
626
|
-
VALUE warning;
|
|
627
|
-
if (state->parser) { // line and columns can't be accurate in resumable
|
|
628
|
-
warning = rb_utf8_str_new_cstr(message);
|
|
629
|
-
} else {
|
|
630
|
-
long line, column;
|
|
631
|
-
cursor_position(state, &line, &column);
|
|
632
|
-
warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
|
|
633
|
-
}
|
|
634
|
-
rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
615
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
|
638
616
|
|
|
639
617
|
static VALUE build_parse_error_message(const char *format, JSON_ParserState *state)
|
|
@@ -673,11 +651,42 @@ static VALUE build_parse_error_message(const char *format, JSON_ParserState *sta
|
|
|
673
651
|
return rb_enc_sprintf(enc_utf8, format, ptr);
|
|
674
652
|
}
|
|
675
653
|
|
|
654
|
+
static VALUE json_path_new(JSON_ParserState *state, VALUE duplicate_key)
|
|
655
|
+
{
|
|
656
|
+
VALUE path = rb_ary_new_capa(state->current_nesting);
|
|
657
|
+
|
|
658
|
+
json_frame_stack *frames = state->frames;
|
|
659
|
+
rvalue_stack *values = state->value_stack;
|
|
660
|
+
|
|
661
|
+
for (long depth = 1; depth < frames->head; depth++) {
|
|
662
|
+
json_frame *frame = &frames->ptr[depth];
|
|
663
|
+
|
|
664
|
+
bool innermost = depth == frames->head - 1;
|
|
665
|
+
long child_head = innermost ? values->head : frames->ptr[depth + 1].value_stack_head;
|
|
666
|
+
long count = child_head - frame->value_stack_head;
|
|
667
|
+
|
|
668
|
+
if (frame->type == JSON_FRAME_ARRAY) {
|
|
669
|
+
rb_ary_push(path, LONG2NUM(frame->phase == JSON_PHASE_ARRAY_COMMA ? count - 1 : count));
|
|
670
|
+
} else if (innermost && !UNDEF_P(duplicate_key)) {
|
|
671
|
+
rb_ary_push(path, duplicate_key);
|
|
672
|
+
} else if (count & 1) {
|
|
673
|
+
rb_ary_push(path, values->ptr[child_head - 1]);
|
|
674
|
+
} else if (frame->phase == JSON_PHASE_OBJECT_COMMA && count >= 2) {
|
|
675
|
+
rb_ary_push(path, values->ptr[child_head - 2]);
|
|
676
|
+
} else {
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return path;
|
|
682
|
+
}
|
|
683
|
+
|
|
676
684
|
static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, long column, bool eos)
|
|
677
685
|
{
|
|
678
686
|
VALUE exc = rb_exc_new_str(eParserError, message);
|
|
679
687
|
rb_ivar_set(exc, i_at_line, LONG2NUM(line));
|
|
680
688
|
rb_ivar_set(exc, i_at_column, LONG2NUM(column));
|
|
689
|
+
rb_ivar_set(exc, i_at_json_path, json_path_new(state, Qundef));
|
|
681
690
|
return exc;
|
|
682
691
|
}
|
|
683
692
|
|
|
@@ -772,11 +781,10 @@ static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const
|
|
|
772
781
|
|
|
773
782
|
static const rb_data_type_t JSON_ParserConfig_type;
|
|
774
783
|
|
|
775
|
-
const char *COMMENT_DEPRECATION_MESSAGE = "Encountered comment in JSON. This will raise an error in json 3.0 unless enabled via `allow_comments: true`";
|
|
776
784
|
NOINLINE(static) void
|
|
777
785
|
json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char *resume_pos)
|
|
778
786
|
{
|
|
779
|
-
if (config->
|
|
787
|
+
if (!config->allow_comments) {
|
|
780
788
|
raise_syntax_error("unexpected token %s", state);
|
|
781
789
|
}
|
|
782
790
|
|
|
@@ -826,11 +834,6 @@ json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char
|
|
|
826
834
|
raise_parse_error_at("unexpected token %s", state, eos(state) ? rewind_pos : start, eos(state));
|
|
827
835
|
break;
|
|
828
836
|
}
|
|
829
|
-
|
|
830
|
-
if (config->on_comment == JSON_DEPRECATED && state->emitted_deprecations < MAX_DEPRECATIONS) {
|
|
831
|
-
state->emitted_deprecations++;
|
|
832
|
-
emit_parse_warning(COMMENT_DEPRECATION_MESSAGE, state);
|
|
833
|
-
}
|
|
834
837
|
}
|
|
835
838
|
|
|
836
839
|
ALWAYS_INLINE(static) void
|
|
@@ -1038,6 +1041,9 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
1038
1041
|
raise_syntax_error_at("incomplete surrogate pair at %s", state, p);
|
|
1039
1042
|
break;
|
|
1040
1043
|
}
|
|
1044
|
+
} else if ((ch & 0xFC00) == 0xDC00) {
|
|
1045
|
+
raise_syntax_error_at("unpaired trailing surrogate at %s", state, p);
|
|
1046
|
+
break;
|
|
1041
1047
|
}
|
|
1042
1048
|
|
|
1043
1049
|
int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
|
|
@@ -1082,7 +1088,8 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
1082
1088
|
return result;
|
|
1083
1089
|
}
|
|
1084
1090
|
|
|
1085
|
-
#define MAX_FAST_INTEGER_SIZE
|
|
1091
|
+
#define MAX_FAST_INTEGER_SIZE 19
|
|
1092
|
+
#define MAX_FAST_UINT64_SIZE 20
|
|
1086
1093
|
#define MAX_NUMBER_STACK_BUFFER 128
|
|
1087
1094
|
|
|
1088
1095
|
typedef VALUE (*json_number_decode_func_t)(const char *ptr);
|
|
@@ -1117,11 +1124,30 @@ NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
|
|
|
1117
1124
|
|
|
1118
1125
|
static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
|
|
1119
1126
|
{
|
|
1120
|
-
if (RB_LIKELY(mantissa_digits
|
|
1121
|
-
if (negative) {
|
|
1127
|
+
if (RB_LIKELY(mantissa_digits <= MAX_FAST_INTEGER_SIZE)) {
|
|
1128
|
+
if (RB_LIKELY(!negative)) {
|
|
1129
|
+
return UINT64T2NUM(mantissa);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// For a negative number 19 digits in length, we only get half of the range,
|
|
1133
|
+
// so ensure this negative number is less than INT64_MAX.
|
|
1134
|
+
//
|
|
1135
|
+
// Note: This does miss INT64_MIN as it's value is one past INT64_MAX
|
|
1136
|
+
// when converted to a uint64_t. It will still be parsed correctly by
|
|
1137
|
+
// falling through to json_decode_large_integer.
|
|
1138
|
+
if (RB_LIKELY(mantissa <= (uint64_t)INT64_MAX)) {
|
|
1122
1139
|
return INT64T2NUM(-((int64_t)mantissa));
|
|
1123
1140
|
}
|
|
1124
|
-
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (!negative && mantissa_digits == MAX_FAST_UINT64_SIZE) {
|
|
1144
|
+
// Not all 20 digit integers can be safely represented by a uint64_t but
|
|
1145
|
+
// some can. The memcmp with uint64_max is safe as we've rejected leading
|
|
1146
|
+
// zeros and we have guaranteed we're comparing it with a 20 digit number.
|
|
1147
|
+
static const char uint64_max[] = "18446744073709551615";
|
|
1148
|
+
if (memcmp(end - MAX_FAST_UINT64_SIZE, uint64_max, MAX_FAST_UINT64_SIZE) <= 0) {
|
|
1149
|
+
return UINT64T2NUM(mantissa);
|
|
1150
|
+
}
|
|
1125
1151
|
}
|
|
1126
1152
|
|
|
1127
1153
|
return json_decode_large_integer(start, end - start);
|
|
@@ -1199,17 +1225,6 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
|
|
1199
1225
|
return Qfalse;
|
|
1200
1226
|
}
|
|
1201
1227
|
|
|
1202
|
-
NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
|
1203
|
-
{
|
|
1204
|
-
VALUE message = rb_sprintf(
|
|
1205
|
-
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
|
|
1206
|
-
rb_inspect(duplicate_key)
|
|
1207
|
-
);
|
|
1208
|
-
|
|
1209
|
-
emit_parse_warning(RSTRING_PTR(message), state);
|
|
1210
|
-
RB_GC_GUARD(message);
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
1228
|
NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
1214
1229
|
{
|
|
1215
1230
|
VALUE message = rb_sprintf(
|
|
@@ -1218,35 +1233,24 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d
|
|
|
1218
1233
|
);
|
|
1219
1234
|
|
|
1220
1235
|
rb_str_concat(message, build_parse_error_message("", state));
|
|
1236
|
+
VALUE exc;
|
|
1221
1237
|
if (state->parser) { // line and columns can't be accurate in resumable
|
|
1222
|
-
|
|
1238
|
+
exc = parse_error_new(state, message, 0, 0, false);
|
|
1223
1239
|
} else {
|
|
1224
1240
|
long line, column;
|
|
1225
1241
|
cursor_position(state, &line, &column);
|
|
1226
1242
|
rb_str_catf(message, " at line %ld column %ld", line, column);
|
|
1227
|
-
|
|
1243
|
+
exc = parse_error_new(state, message, line, column, false);
|
|
1228
1244
|
}
|
|
1245
|
+
rb_ivar_set(exc, i_at_json_path, json_path_new(state, duplicate_key));
|
|
1246
|
+
rb_exc_raise(exc);
|
|
1229
1247
|
}
|
|
1230
1248
|
|
|
1231
1249
|
NOINLINE(static) void json_on_duplicate_key(JSON_ParserState *state, JSON_ParserConfig *config, size_t count, const VALUE *pairs)
|
|
1232
1250
|
{
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
return;
|
|
1236
|
-
|
|
1237
|
-
case JSON_DEPRECATED:
|
|
1238
|
-
// Only emit the first few deprecations to avoid spamming.
|
|
1239
|
-
if (state->emitted_deprecations < MAX_DEPRECATIONS) {
|
|
1240
|
-
state->emitted_deprecations++;
|
|
1241
|
-
emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
|
|
1242
|
-
}
|
|
1243
|
-
return;
|
|
1244
|
-
|
|
1245
|
-
case JSON_RAISE:
|
|
1246
|
-
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
|
1247
|
-
return;
|
|
1251
|
+
if (!config->allow_duplicate_key) {
|
|
1252
|
+
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
|
1248
1253
|
}
|
|
1249
|
-
UNREACHABLE;
|
|
1250
1254
|
}
|
|
1251
1255
|
|
|
1252
1256
|
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
|
|
@@ -2012,13 +2016,13 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
2012
2016
|
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
2013
2017
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
2014
2018
|
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
2015
|
-
else if (key == sym_allow_comments) { config->
|
|
2019
|
+
else if (key == sym_allow_comments) { config->allow_comments = RTEST(val); }
|
|
2016
2020
|
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
2017
2021
|
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
2018
2022
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
2019
2023
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
2020
2024
|
else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
|
|
2021
|
-
else if (key == sym_allow_duplicate_key) { config->
|
|
2025
|
+
else if (key == sym_allow_duplicate_key) { config->allow_duplicate_key = RTEST(val); }
|
|
2022
2026
|
else if (key == sym_decimal_class) {
|
|
2023
2027
|
if (RTEST(val)) {
|
|
2024
2028
|
if (rb_respond_to(val, i_try_convert)) {
|
|
@@ -2048,7 +2052,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
2048
2052
|
}
|
|
2049
2053
|
}
|
|
2050
2054
|
}
|
|
2051
|
-
else
|
|
2055
|
+
else {
|
|
2052
2056
|
if (!args->unknown_keywords) {
|
|
2053
2057
|
args->unknown_keywords = rb_obj_hide(rb_ary_new());
|
|
2054
2058
|
}
|
|
@@ -2076,15 +2080,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self
|
|
|
2076
2080
|
// the provided keys than to check all possible keys.
|
|
2077
2081
|
rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
|
|
2078
2082
|
|
|
2079
|
-
|
|
2080
|
-
if (RARRAY_LEN(args.unknown_keywords) == 1) {
|
|
2081
|
-
rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, RARRAY_AREF(args.unknown_keywords, 0));
|
|
2082
|
-
}
|
|
2083
|
-
else {
|
|
2084
|
-
VALUE keywords = rb_ary_join(args.unknown_keywords, rb_utf8_str_new_cstr(", "));
|
|
2085
|
-
rb_raise(rb_eArgError, "unknown keywords: %" PRIsVALUE, keywords);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2083
|
+
raise_argument_error_on_unknown_keywords(args.unknown_keywords);
|
|
2088
2084
|
}
|
|
2089
2085
|
|
|
2090
2086
|
/*
|
|
@@ -2163,6 +2159,12 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2163
2159
|
// the rvalue stack.
|
|
2164
2160
|
VALUE result = complete ? *rvalue_stack_peek(state->value_stack, 1) : Qundef;
|
|
2165
2161
|
|
|
2162
|
+
if (complete) {
|
|
2163
|
+
json_ensure_eof(state, config);
|
|
2164
|
+
} else {
|
|
2165
|
+
raise_eos_error("unexpected end of input", state);
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2166
2168
|
// This may be skipped in case of exception, but
|
|
2167
2169
|
// it won't cause a leak.
|
|
2168
2170
|
rvalue_stack_eagerly_release(value_stack_handle);
|
|
@@ -2171,12 +2173,6 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2171
2173
|
RB_GC_GUARD(frame_stack_handle);
|
|
2172
2174
|
RB_GC_GUARD(Vsource);
|
|
2173
2175
|
|
|
2174
|
-
if (complete) {
|
|
2175
|
-
json_ensure_eof(state, config);
|
|
2176
|
-
} else {
|
|
2177
|
-
raise_eos_error("unexpected end of input", state);
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
2176
|
return result;
|
|
2181
2177
|
}
|
|
2182
2178
|
|
|
@@ -2646,7 +2642,6 @@ static VALUE cResumableParser_clear(VALUE self)
|
|
|
2646
2642
|
parser->state.name_cache.length = 0;
|
|
2647
2643
|
parser->state.current_nesting = 0;
|
|
2648
2644
|
parser->state.in_array = 1;
|
|
2649
|
-
parser->state.emitted_deprecations = 0;
|
|
2650
2645
|
parser->state.start = parser->state.cursor = parser->state.end = NULL;
|
|
2651
2646
|
return self;
|
|
2652
2647
|
}
|
|
@@ -2913,6 +2908,7 @@ void Init_parser(void)
|
|
|
2913
2908
|
i_encode = rb_intern("encode");
|
|
2914
2909
|
i_at_line = rb_intern("@line");
|
|
2915
2910
|
i_at_column = rb_intern("@column");
|
|
2911
|
+
i_at_json_path = rb_intern("@json_path");
|
|
2916
2912
|
|
|
2917
2913
|
binary_encindex = rb_ascii8bit_encindex();
|
|
2918
2914
|
utf8_encindex = rb_utf8_encindex();
|