json 2.21.2 → 3.1.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 +64 -1
- data/README.md +4 -84
- data/ext/json/ext/fbuffer/fbuffer.h +4 -0
- data/ext/json/ext/generator/generator.c +187 -81
- data/ext/json/ext/json.h +19 -0
- data/ext/json/ext/parser/parser.c +139 -138
- data/lib/json/common.rb +153 -412
- data/lib/json/ext/generator/state.rb +2 -33
- data/lib/json/ext.rb +129 -5
- data/lib/json/truffle_ruby/generator.rb +140 -102
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +51 -295
- metadata +2 -18
- 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
|
|
@@ -20,9 +20,9 @@ static int utf8_encindex;
|
|
|
20
20
|
#ifndef HAVE_RB_HASH_BULK_INSERT
|
|
21
21
|
// For TruffleRuby
|
|
22
22
|
static void
|
|
23
|
-
rb_hash_bulk_insert(
|
|
23
|
+
rb_hash_bulk_insert(rb_len_t count, const VALUE *pairs, VALUE hash)
|
|
24
24
|
{
|
|
25
|
-
|
|
25
|
+
rb_len_t index = 0;
|
|
26
26
|
while (index < count) {
|
|
27
27
|
VALUE name = pairs[index++];
|
|
28
28
|
VALUE value = pairs[index++];
|
|
@@ -79,7 +79,7 @@ static rb_encoding *enc_utf8;
|
|
|
79
79
|
|
|
80
80
|
#define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55
|
|
81
81
|
|
|
82
|
-
static inline VALUE build_interned_string(const char *str, const
|
|
82
|
+
static inline VALUE build_interned_string(const char *str, const rb_len_t length)
|
|
83
83
|
{
|
|
84
84
|
# ifdef HAVE_RB_ENC_INTERNED_STR
|
|
85
85
|
return rb_enc_interned_str(str, length, enc_utf8);
|
|
@@ -89,7 +89,7 @@ static inline VALUE build_interned_string(const char *str, const long length)
|
|
|
89
89
|
# endif
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
static inline VALUE build_symbol(const char *str, const
|
|
92
|
+
static inline VALUE build_symbol(const char *str, const rb_len_t length)
|
|
93
93
|
{
|
|
94
94
|
return rb_str_intern(build_interned_string(str, length));
|
|
95
95
|
}
|
|
@@ -138,7 +138,7 @@ ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr
|
|
|
138
138
|
ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
|
|
139
139
|
{
|
|
140
140
|
const char *rstring_ptr;
|
|
141
|
-
|
|
141
|
+
rb_len_t rstring_length;
|
|
142
142
|
|
|
143
143
|
RSTRING_GETMEM(rstring, rstring_ptr, rstring_length);
|
|
144
144
|
|
|
@@ -323,14 +323,17 @@ static const rb_data_type_t JSON_Parser_rvalue_stack_type = {
|
|
|
323
323
|
static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref)
|
|
324
324
|
{
|
|
325
325
|
rvalue_stack *stack;
|
|
326
|
-
|
|
327
|
-
*stack_ref = stack;
|
|
326
|
+
VALUE stack_handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack);
|
|
328
327
|
MEMCPY(stack, old_stack, rvalue_stack, 1);
|
|
329
328
|
|
|
330
329
|
stack->capa = old_stack->capa << 1;
|
|
331
330
|
stack->ptr = ALLOC_N(VALUE, stack->capa);
|
|
332
331
|
stack->type = RVALUE_STACK_HEAP_ALLOCATED;
|
|
333
332
|
MEMCPY(stack->ptr, old_stack->ptr, VALUE, old_stack->head);
|
|
333
|
+
|
|
334
|
+
*handle = stack_handle;
|
|
335
|
+
*stack_ref = stack;
|
|
336
|
+
|
|
334
337
|
return stack;
|
|
335
338
|
}
|
|
336
339
|
|
|
@@ -403,19 +406,13 @@ typedef struct json_frame_stack_struct {
|
|
|
403
406
|
json_frame *ptr;
|
|
404
407
|
} json_frame_stack;
|
|
405
408
|
|
|
406
|
-
enum deprecatable_action {
|
|
407
|
-
JSON_DEPRECATED = 0,
|
|
408
|
-
JSON_IGNORE,
|
|
409
|
-
JSON_RAISE,
|
|
410
|
-
};
|
|
411
|
-
|
|
412
409
|
typedef struct JSON_ParserStruct {
|
|
413
410
|
VALUE on_load_proc;
|
|
414
411
|
VALUE decimal_class;
|
|
415
412
|
ID decimal_method_id;
|
|
416
|
-
enum deprecatable_action on_duplicate_key;
|
|
417
|
-
enum deprecatable_action on_comment;
|
|
418
413
|
int max_nesting;
|
|
414
|
+
bool allow_comments;
|
|
415
|
+
bool allow_duplicate_key;
|
|
419
416
|
bool allow_nan;
|
|
420
417
|
bool allow_trailing_comma;
|
|
421
418
|
bool allow_control_characters;
|
|
@@ -435,7 +432,6 @@ typedef struct JSON_ParserStateStruct {
|
|
|
435
432
|
rvalue_cache name_cache;
|
|
436
433
|
int in_array;
|
|
437
434
|
int current_nesting;
|
|
438
|
-
unsigned int emitted_deprecations;
|
|
439
435
|
VALUE parser;
|
|
440
436
|
} JSON_ParserState;
|
|
441
437
|
|
|
@@ -587,7 +583,7 @@ static inline char peek(JSON_ParserState *state)
|
|
|
587
583
|
return *state->cursor;
|
|
588
584
|
}
|
|
589
585
|
|
|
590
|
-
static void cursor_position(JSON_ParserState *state, long *line_out,
|
|
586
|
+
static void cursor_position(JSON_ParserState *state, long *line_out, rb_len_t *column_out)
|
|
591
587
|
{
|
|
592
588
|
JSON_ASSERT(!state->parser);
|
|
593
589
|
JSON_ASSERT(state->cursor);
|
|
@@ -599,19 +595,15 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum
|
|
|
599
595
|
}
|
|
600
596
|
|
|
601
597
|
const char *cursor = state->cursor;
|
|
602
|
-
long column = 0;
|
|
603
598
|
long line = 1;
|
|
604
599
|
|
|
605
|
-
while (cursor
|
|
606
|
-
|
|
607
|
-
line++;
|
|
608
|
-
break;
|
|
609
|
-
}
|
|
610
|
-
column++;
|
|
600
|
+
while (cursor > state->start && cursor[-1] != '\n') {
|
|
601
|
+
cursor--;
|
|
611
602
|
}
|
|
603
|
+
rb_len_t column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1;
|
|
612
604
|
|
|
613
|
-
while (cursor
|
|
614
|
-
if (
|
|
605
|
+
while (cursor > state->start) {
|
|
606
|
+
if (*--cursor == '\n') {
|
|
615
607
|
line++;
|
|
616
608
|
}
|
|
617
609
|
}
|
|
@@ -619,21 +611,6 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum
|
|
|
619
611
|
*column_out = column;
|
|
620
612
|
}
|
|
621
613
|
|
|
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
614
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
|
638
615
|
|
|
639
616
|
static VALUE build_parse_error_message(const char *format, JSON_ParserState *state)
|
|
@@ -653,17 +630,15 @@ static VALUE build_parse_error_message(const char *format, JSON_ParserState *sta
|
|
|
653
630
|
}
|
|
654
631
|
|
|
655
632
|
if (len) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
if (buffer[len] >= 0xC0) { // multibyte character start
|
|
664
|
-
len--;
|
|
633
|
+
if (len == PARSE_ERROR_FRAGMENT_LEN) {
|
|
634
|
+
// Only trim when the byte limit splits a multibyte character.
|
|
635
|
+
while (len && (unsigned char)ptr[len] >= 0x80 && (unsigned char)ptr[len] < 0xC0) {
|
|
636
|
+
len--;
|
|
637
|
+
}
|
|
665
638
|
}
|
|
666
639
|
|
|
640
|
+
buffer[0] = '\'';
|
|
641
|
+
MEMCPY(buffer + 1, ptr, char, len);
|
|
667
642
|
buffer[len + 1] = '\'';
|
|
668
643
|
buffer[len + 2] = '\0';
|
|
669
644
|
ptr = (const char *)buffer;
|
|
@@ -673,11 +648,42 @@ static VALUE build_parse_error_message(const char *format, JSON_ParserState *sta
|
|
|
673
648
|
return rb_enc_sprintf(enc_utf8, format, ptr);
|
|
674
649
|
}
|
|
675
650
|
|
|
676
|
-
static VALUE
|
|
651
|
+
static VALUE json_path_new(JSON_ParserState *state, VALUE duplicate_key)
|
|
652
|
+
{
|
|
653
|
+
VALUE path = rb_ary_new_capa(state->current_nesting);
|
|
654
|
+
|
|
655
|
+
json_frame_stack *frames = state->frames;
|
|
656
|
+
rvalue_stack *values = state->value_stack;
|
|
657
|
+
|
|
658
|
+
for (long depth = 1; depth < frames->head; depth++) {
|
|
659
|
+
json_frame *frame = &frames->ptr[depth];
|
|
660
|
+
|
|
661
|
+
bool innermost = depth == frames->head - 1;
|
|
662
|
+
long child_head = innermost ? values->head : frames->ptr[depth + 1].value_stack_head;
|
|
663
|
+
long count = child_head - frame->value_stack_head;
|
|
664
|
+
|
|
665
|
+
if (frame->type == JSON_FRAME_ARRAY) {
|
|
666
|
+
rb_ary_push(path, LONG2NUM(frame->phase == JSON_PHASE_ARRAY_COMMA ? count - 1 : count));
|
|
667
|
+
} else if (innermost && !UNDEF_P(duplicate_key)) {
|
|
668
|
+
rb_ary_push(path, duplicate_key);
|
|
669
|
+
} else if (count & 1) {
|
|
670
|
+
rb_ary_push(path, values->ptr[child_head - 1]);
|
|
671
|
+
} else if (frame->phase == JSON_PHASE_OBJECT_COMMA && count >= 2) {
|
|
672
|
+
rb_ary_push(path, values->ptr[child_head - 2]);
|
|
673
|
+
} else {
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return path;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, rb_len_t column, bool eos)
|
|
677
682
|
{
|
|
678
683
|
VALUE exc = rb_exc_new_str(eParserError, message);
|
|
679
684
|
rb_ivar_set(exc, i_at_line, LONG2NUM(line));
|
|
680
|
-
rb_ivar_set(exc, i_at_column,
|
|
685
|
+
rb_ivar_set(exc, i_at_column, LEN2NUM(column));
|
|
686
|
+
rb_ivar_set(exc, i_at_json_path, json_path_new(state, Qundef));
|
|
681
687
|
return exc;
|
|
682
688
|
}
|
|
683
689
|
|
|
@@ -694,9 +700,10 @@ NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *st
|
|
|
694
700
|
}
|
|
695
701
|
} else {
|
|
696
702
|
VALUE message = build_parse_error_message(format, state);
|
|
697
|
-
long line
|
|
703
|
+
long line;
|
|
704
|
+
rb_len_t column;
|
|
698
705
|
cursor_position(state, &line, &column);
|
|
699
|
-
rb_str_catf(message, " at line %ld column %
|
|
706
|
+
rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column);
|
|
700
707
|
rb_exc_raise(parse_error_new(state, message, line, column, eos));
|
|
701
708
|
}
|
|
702
709
|
}
|
|
@@ -749,7 +756,7 @@ static const signed char digit_values[256] = {
|
|
|
749
756
|
static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
|
|
750
757
|
{
|
|
751
758
|
if (RB_UNLIKELY(sp > spe - 4)) {
|
|
752
|
-
|
|
759
|
+
raise_syntax_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
753
760
|
}
|
|
754
761
|
|
|
755
762
|
const unsigned char *p = (const unsigned char *)sp;
|
|
@@ -772,11 +779,10 @@ static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const
|
|
|
772
779
|
|
|
773
780
|
static const rb_data_type_t JSON_ParserConfig_type;
|
|
774
781
|
|
|
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
782
|
NOINLINE(static) void
|
|
777
783
|
json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char *resume_pos)
|
|
778
784
|
{
|
|
779
|
-
if (config->
|
|
785
|
+
if (!config->allow_comments) {
|
|
780
786
|
raise_syntax_error("unexpected token %s", state);
|
|
781
787
|
}
|
|
782
788
|
|
|
@@ -826,11 +832,6 @@ json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char
|
|
|
826
832
|
raise_parse_error_at("unexpected token %s", state, eos(state) ? rewind_pos : start, eos(state));
|
|
827
833
|
break;
|
|
828
834
|
}
|
|
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
835
|
}
|
|
835
836
|
|
|
836
837
|
ALWAYS_INLINE(static) void
|
|
@@ -892,12 +893,12 @@ static inline VALUE build_string(const char *start, const char *end, bool intern
|
|
|
892
893
|
VALUE result;
|
|
893
894
|
# ifdef HAVE_RB_ENC_INTERNED_STR
|
|
894
895
|
if (intern) {
|
|
895
|
-
result = rb_enc_interned_str(start, (
|
|
896
|
+
result = rb_enc_interned_str(start, (rb_len_t)(end - start), enc_utf8);
|
|
896
897
|
} else {
|
|
897
|
-
result = rb_utf8_str_new(start, (
|
|
898
|
+
result = rb_utf8_str_new(start, (rb_len_t)(end - start));
|
|
898
899
|
}
|
|
899
900
|
# else
|
|
900
|
-
result = rb_utf8_str_new(start, (
|
|
901
|
+
result = rb_utf8_str_new(start, (rb_len_t)(end - start));
|
|
901
902
|
if (intern) {
|
|
902
903
|
result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
|
|
903
904
|
}
|
|
@@ -1038,6 +1039,9 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
1038
1039
|
raise_syntax_error_at("incomplete surrogate pair at %s", state, p);
|
|
1039
1040
|
break;
|
|
1040
1041
|
}
|
|
1042
|
+
} else if ((ch & 0xFC00) == 0xDC00) {
|
|
1043
|
+
raise_syntax_error_at("unpaired trailing surrogate at %s", state, p);
|
|
1044
|
+
break;
|
|
1041
1045
|
}
|
|
1042
1046
|
|
|
1043
1047
|
int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
|
|
@@ -1045,13 +1049,13 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
1045
1049
|
p = ++pe;
|
|
1046
1050
|
break;
|
|
1047
1051
|
}
|
|
1048
|
-
case 0:
|
|
1049
|
-
return Qundef;
|
|
1050
1052
|
default:
|
|
1051
1053
|
if ((unsigned char)*pe < 0x20) {
|
|
1052
1054
|
if (!config->allow_control_characters) {
|
|
1053
1055
|
if (*pe == '\n') {
|
|
1054
1056
|
raise_syntax_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1);
|
|
1057
|
+
} else if (*pe == '\0') {
|
|
1058
|
+
raise_syntax_error_at("Invalid unescaped NULL character (\\0) in string: %s", state, pe - 1);
|
|
1055
1059
|
}
|
|
1056
1060
|
raise_syntax_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
1057
1061
|
}
|
|
@@ -1082,12 +1086,13 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
1082
1086
|
return result;
|
|
1083
1087
|
}
|
|
1084
1088
|
|
|
1085
|
-
#define MAX_FAST_INTEGER_SIZE
|
|
1089
|
+
#define MAX_FAST_INTEGER_SIZE 19
|
|
1090
|
+
#define MAX_FAST_UINT64_SIZE 20
|
|
1086
1091
|
#define MAX_NUMBER_STACK_BUFFER 128
|
|
1087
1092
|
|
|
1088
1093
|
typedef VALUE (*json_number_decode_func_t)(const char *ptr);
|
|
1089
1094
|
|
|
1090
|
-
static inline VALUE json_decode_large_number(const char *start,
|
|
1095
|
+
static inline VALUE json_decode_large_number(const char *start, rb_len_t len, json_number_decode_func_t func)
|
|
1091
1096
|
{
|
|
1092
1097
|
if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
|
|
1093
1098
|
char buffer[MAX_NUMBER_STACK_BUFFER];
|
|
@@ -1110,18 +1115,37 @@ static VALUE json_decode_inum(const char *buffer)
|
|
|
1110
1115
|
return rb_cstr2inum(buffer, 10);
|
|
1111
1116
|
}
|
|
1112
1117
|
|
|
1113
|
-
NOINLINE(static) VALUE json_decode_large_integer(const char *start,
|
|
1118
|
+
NOINLINE(static) VALUE json_decode_large_integer(const char *start, rb_len_t len)
|
|
1114
1119
|
{
|
|
1115
1120
|
return json_decode_large_number(start, len, json_decode_inum);
|
|
1116
1121
|
}
|
|
1117
1122
|
|
|
1118
1123
|
static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
|
|
1119
1124
|
{
|
|
1120
|
-
if (RB_LIKELY(mantissa_digits
|
|
1121
|
-
if (negative) {
|
|
1125
|
+
if (RB_LIKELY(mantissa_digits <= MAX_FAST_INTEGER_SIZE)) {
|
|
1126
|
+
if (RB_LIKELY(!negative)) {
|
|
1127
|
+
return UINT64T2NUM(mantissa);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// For a negative number 19 digits in length, we only get half of the range,
|
|
1131
|
+
// so ensure this negative number is less than INT64_MAX.
|
|
1132
|
+
//
|
|
1133
|
+
// Note: This does miss INT64_MIN as it's value is one past INT64_MAX
|
|
1134
|
+
// when converted to a uint64_t. It will still be parsed correctly by
|
|
1135
|
+
// falling through to json_decode_large_integer.
|
|
1136
|
+
if (RB_LIKELY(mantissa <= (uint64_t)INT64_MAX)) {
|
|
1122
1137
|
return INT64T2NUM(-((int64_t)mantissa));
|
|
1123
1138
|
}
|
|
1124
|
-
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
if (!negative && mantissa_digits == MAX_FAST_UINT64_SIZE) {
|
|
1142
|
+
// Not all 20 digit integers can be safely represented by a uint64_t but
|
|
1143
|
+
// some can. The memcmp with uint64_max is safe as we've rejected leading
|
|
1144
|
+
// zeros and we have guaranteed we're comparing it with a 20 digit number.
|
|
1145
|
+
static const char uint64_max[] = "18446744073709551615";
|
|
1146
|
+
if (memcmp(end - MAX_FAST_UINT64_SIZE, uint64_max, MAX_FAST_UINT64_SIZE) <= 0) {
|
|
1147
|
+
return UINT64T2NUM(mantissa);
|
|
1148
|
+
}
|
|
1125
1149
|
}
|
|
1126
1150
|
|
|
1127
1151
|
return json_decode_large_integer(start, end - start);
|
|
@@ -1132,7 +1156,7 @@ static VALUE json_decode_dnum(const char *buffer)
|
|
|
1132
1156
|
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
1133
1157
|
}
|
|
1134
1158
|
|
|
1135
|
-
NOINLINE(static) VALUE json_decode_large_float(const char *start,
|
|
1159
|
+
NOINLINE(static) VALUE json_decode_large_float(const char *start, rb_len_t len)
|
|
1136
1160
|
{
|
|
1137
1161
|
return json_decode_large_number(start, len, json_decode_dnum);
|
|
1138
1162
|
}
|
|
@@ -1148,11 +1172,11 @@ static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantis
|
|
|
1148
1172
|
return rb_funcallv(config->decimal_class, config->decimal_method_id, 1, &text);
|
|
1149
1173
|
}
|
|
1150
1174
|
|
|
1151
|
-
if (RB_UNLIKELY(exponent
|
|
1175
|
+
if (RB_UNLIKELY(exponent >= (int64_t)INT32_MAX)) {
|
|
1152
1176
|
return negative ? CMinusInfinity : CInfinity;
|
|
1153
1177
|
}
|
|
1154
1178
|
|
|
1155
|
-
if (RB_UNLIKELY(exponent
|
|
1179
|
+
if (RB_UNLIKELY(exponent <= INT32_MIN)) {
|
|
1156
1180
|
return rb_float_new(negative ? -0.0 : 0.0);
|
|
1157
1181
|
}
|
|
1158
1182
|
|
|
@@ -1199,17 +1223,6 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
|
|
1199
1223
|
return Qfalse;
|
|
1200
1224
|
}
|
|
1201
1225
|
|
|
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
1226
|
NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
1214
1227
|
{
|
|
1215
1228
|
VALUE message = rb_sprintf(
|
|
@@ -1218,35 +1231,25 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d
|
|
|
1218
1231
|
);
|
|
1219
1232
|
|
|
1220
1233
|
rb_str_concat(message, build_parse_error_message("", state));
|
|
1234
|
+
VALUE exc;
|
|
1221
1235
|
if (state->parser) { // line and columns can't be accurate in resumable
|
|
1222
|
-
|
|
1236
|
+
exc = parse_error_new(state, message, 0, 0, false);
|
|
1223
1237
|
} else {
|
|
1224
|
-
long line
|
|
1238
|
+
long line;
|
|
1239
|
+
rb_len_t column;
|
|
1225
1240
|
cursor_position(state, &line, &column);
|
|
1226
|
-
rb_str_catf(message, " at line %ld column %
|
|
1227
|
-
|
|
1241
|
+
rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column);
|
|
1242
|
+
exc = parse_error_new(state, message, line, column, false);
|
|
1228
1243
|
}
|
|
1244
|
+
rb_ivar_set(exc, i_at_json_path, json_path_new(state, duplicate_key));
|
|
1245
|
+
rb_exc_raise(exc);
|
|
1229
1246
|
}
|
|
1230
1247
|
|
|
1231
1248
|
NOINLINE(static) void json_on_duplicate_key(JSON_ParserState *state, JSON_ParserConfig *config, size_t count, const VALUE *pairs)
|
|
1232
1249
|
{
|
|
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;
|
|
1250
|
+
if (!config->allow_duplicate_key) {
|
|
1251
|
+
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
|
|
1248
1252
|
}
|
|
1249
|
-
UNREACHABLE;
|
|
1250
1253
|
}
|
|
1251
1254
|
|
|
1252
1255
|
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
|
|
@@ -1514,7 +1517,7 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
|
|
|
1514
1517
|
}
|
|
1515
1518
|
|
|
1516
1519
|
if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
|
|
1517
|
-
exponent = negative_exponent ?
|
|
1520
|
+
exponent = negative_exponent ? INT32_MIN : INT32_MAX;
|
|
1518
1521
|
} else {
|
|
1519
1522
|
exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
|
|
1520
1523
|
}
|
|
@@ -1811,7 +1814,7 @@ ALWAYS_INLINE(static) bool json_parse_any(JSON_ParserState *state, JSON_ParserCo
|
|
|
1811
1814
|
if (RB_LIKELY(peek(state) == '"')) {
|
|
1812
1815
|
VALUE string = json_parse_string(state, config, true);
|
|
1813
1816
|
if (UNDEF_P(string)) {
|
|
1814
|
-
if (resumable) {
|
|
1817
|
+
if (resumable && eos(state)) {
|
|
1815
1818
|
state->cursor = start;
|
|
1816
1819
|
return false;
|
|
1817
1820
|
} else {
|
|
@@ -2012,13 +2015,21 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
2012
2015
|
if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
|
|
2013
2016
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
2014
2017
|
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
2015
|
-
else if (key == sym_allow_comments) { config->
|
|
2018
|
+
else if (key == sym_allow_comments) { config->allow_comments = RTEST(val); }
|
|
2016
2019
|
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
2017
2020
|
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
2018
2021
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
2019
2022
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
2020
|
-
else if (key == sym_on_load) {
|
|
2021
|
-
|
|
2023
|
+
else if (key == sym_on_load) {
|
|
2024
|
+
if (RTEST(val) && !rb_obj_is_proc(val)) {
|
|
2025
|
+
val = rb_check_funcall(val, rb_intern("to_proc"), 0, NULL);
|
|
2026
|
+
if (val == Qundef || !rb_obj_is_proc(val)) {
|
|
2027
|
+
rb_raise(rb_eTypeError, "on_load must be a Proc");
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse);
|
|
2031
|
+
}
|
|
2032
|
+
else if (key == sym_allow_duplicate_key) { config->allow_duplicate_key = RTEST(val); }
|
|
2022
2033
|
else if (key == sym_decimal_class) {
|
|
2023
2034
|
if (RTEST(val)) {
|
|
2024
2035
|
if (rb_respond_to(val, i_try_convert)) {
|
|
@@ -2037,8 +2048,8 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
2037
2048
|
parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
|
|
2038
2049
|
|
|
2039
2050
|
const char *method_name_beg = last_colon + 1;
|
|
2040
|
-
|
|
2041
|
-
|
|
2051
|
+
rb_len_t before_len = method_name_beg - name_cstr;
|
|
2052
|
+
rb_len_t len = RSTRING_LEN(name) - before_len;
|
|
2042
2053
|
VALUE method_name = rb_str_substr(name, before_len, len);
|
|
2043
2054
|
config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
|
|
2044
2055
|
} else {
|
|
@@ -2048,7 +2059,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
2048
2059
|
}
|
|
2049
2060
|
}
|
|
2050
2061
|
}
|
|
2051
|
-
else
|
|
2062
|
+
else {
|
|
2052
2063
|
if (!args->unknown_keywords) {
|
|
2053
2064
|
args->unknown_keywords = rb_obj_hide(rb_ary_new());
|
|
2054
2065
|
}
|
|
@@ -2076,15 +2087,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self
|
|
|
2076
2087
|
// the provided keys than to check all possible keys.
|
|
2077
2088
|
rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
|
|
2078
2089
|
|
|
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
|
-
}
|
|
2090
|
+
raise_argument_error_on_unknown_keywords(args.unknown_keywords);
|
|
2088
2091
|
}
|
|
2089
2092
|
|
|
2090
2093
|
/*
|
|
@@ -2113,9 +2116,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2113
2116
|
// Ensure the string isn't mutated under us.
|
|
2114
2117
|
// The classic API to use is `rb_str_locktmp`, but then we'd
|
|
2115
2118
|
// need to use `rb_protect` to make sure we always unlock.
|
|
2116
|
-
|
|
2117
|
-
Vsource = rb_str_new_frozen(Vsource);
|
|
2118
|
-
}
|
|
2119
|
+
Vsource = rb_str_new_frozen(Vsource);
|
|
2119
2120
|
|
|
2120
2121
|
VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
|
|
2121
2122
|
rvalue_stack value_stack = {
|
|
@@ -2139,7 +2140,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2139
2140
|
.head = 1,
|
|
2140
2141
|
};
|
|
2141
2142
|
|
|
2142
|
-
|
|
2143
|
+
rb_len_t len;
|
|
2143
2144
|
const char *start;
|
|
2144
2145
|
|
|
2145
2146
|
RSTRING_GETMEM(Vsource, start, len);
|
|
@@ -2163,6 +2164,12 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2163
2164
|
// the rvalue stack.
|
|
2164
2165
|
VALUE result = complete ? *rvalue_stack_peek(state->value_stack, 1) : Qundef;
|
|
2165
2166
|
|
|
2167
|
+
if (complete) {
|
|
2168
|
+
json_ensure_eof(state, config);
|
|
2169
|
+
} else {
|
|
2170
|
+
raise_eos_error("unexpected end of input", state);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2166
2173
|
// This may be skipped in case of exception, but
|
|
2167
2174
|
// it won't cause a leak.
|
|
2168
2175
|
rvalue_stack_eagerly_release(value_stack_handle);
|
|
@@ -2171,12 +2178,6 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
|
2171
2178
|
RB_GC_GUARD(frame_stack_handle);
|
|
2172
2179
|
RB_GC_GUARD(Vsource);
|
|
2173
2180
|
|
|
2174
|
-
if (complete) {
|
|
2175
|
-
json_ensure_eof(state, config);
|
|
2176
|
-
} else {
|
|
2177
|
-
raise_eos_error("unexpected end of input", state);
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
2181
|
return result;
|
|
2181
2182
|
}
|
|
2182
2183
|
|
|
@@ -2458,7 +2459,7 @@ static VALUE cResumableParser_feed(VALUE self, VALUE str)
|
|
|
2458
2459
|
rb_str_append(parser->buffer, str);
|
|
2459
2460
|
}
|
|
2460
2461
|
|
|
2461
|
-
|
|
2462
|
+
rb_len_t len;
|
|
2462
2463
|
const char *start;
|
|
2463
2464
|
RSTRING_GETMEM(parser->buffer, start, len);
|
|
2464
2465
|
parser->state.start = start;
|
|
@@ -2646,7 +2647,6 @@ static VALUE cResumableParser_clear(VALUE self)
|
|
|
2646
2647
|
parser->state.name_cache.length = 0;
|
|
2647
2648
|
parser->state.current_nesting = 0;
|
|
2648
2649
|
parser->state.in_array = 1;
|
|
2649
|
-
parser->state.emitted_deprecations = 0;
|
|
2650
2650
|
parser->state.start = parser->state.cursor = parser->state.end = NULL;
|
|
2651
2651
|
return self;
|
|
2652
2652
|
}
|
|
@@ -2765,7 +2765,7 @@ static VALUE cResumableParser_rest(VALUE self)
|
|
|
2765
2765
|
|
|
2766
2766
|
size_t offset = parser->state.cursor - parser->state.start;
|
|
2767
2767
|
const char *ptr;
|
|
2768
|
-
|
|
2768
|
+
rb_len_t len;
|
|
2769
2769
|
RSTRING_GETMEM(parser->buffer, ptr, len);
|
|
2770
2770
|
return rb_utf8_str_new(ptr + offset, len - offset);
|
|
2771
2771
|
}
|
|
@@ -2913,6 +2913,7 @@ void Init_parser(void)
|
|
|
2913
2913
|
i_encode = rb_intern("encode");
|
|
2914
2914
|
i_at_line = rb_intern("@line");
|
|
2915
2915
|
i_at_column = rb_intern("@column");
|
|
2916
|
+
i_at_json_path = rb_intern("@json_path");
|
|
2916
2917
|
|
|
2917
2918
|
binary_encindex = rb_ascii8bit_encindex();
|
|
2918
2919
|
utf8_encindex = rb_utf8_encindex();
|