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.
@@ -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(long count, const VALUE *pairs, VALUE hash)
23
+ rb_hash_bulk_insert(rb_len_t count, const VALUE *pairs, VALUE hash)
24
24
  {
25
- long index = 0;
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 long length)
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 long length)
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
- long rstring_length;
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
- *handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack);
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, long *column_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 >= state->start) {
606
- if (*cursor-- == '\n') {
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 >= state->start) {
614
- if (*cursor-- == '\n') {
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
- buffer[0] = '\'';
657
- MEMCPY(buffer + 1, ptr, char, len);
658
-
659
- while (buffer[len] >= 0x80 && buffer[len] < 0xC0) { // Is continuation byte
660
- len--;
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 parse_error_new(JSON_ParserState *state, VALUE message, long line, long column, bool eos)
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, LONG2NUM(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, column;
703
+ long line;
704
+ rb_len_t column;
698
705
  cursor_position(state, &line, &column);
699
- rb_str_catf(message, " at line %ld column %ld", line, 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
- raise_eos_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
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->on_comment == JSON_RAISE) {
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, (long)(end - start), enc_utf8);
896
+ result = rb_enc_interned_str(start, (rb_len_t)(end - start), enc_utf8);
896
897
  } else {
897
- result = rb_utf8_str_new(start, (long)(end - 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, (long)(end - 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 18
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, long len, json_number_decode_func_t func)
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, long len)
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 < MAX_FAST_INTEGER_SIZE)) {
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
- return UINT64T2NUM(mantissa);
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, long len)
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 > INT32_MAX)) {
1175
+ if (RB_UNLIKELY(exponent >= (int64_t)INT32_MAX)) {
1152
1176
  return negative ? CMinusInfinity : CInfinity;
1153
1177
  }
1154
1178
 
1155
- if (RB_UNLIKELY(exponent < INT32_MIN)) {
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
- rb_exc_raise(parse_error_new(state, message, 0, 0, false));
1236
+ exc = parse_error_new(state, message, 0, 0, false);
1223
1237
  } else {
1224
- long line, column;
1238
+ long line;
1239
+ rb_len_t column;
1225
1240
  cursor_position(state, &line, &column);
1226
- rb_str_catf(message, " at line %ld column %ld", line, column);
1227
- rb_exc_raise(parse_error_new(state, message, line, column, false));
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
- switch (config->on_duplicate_key) {
1234
- case JSON_IGNORE:
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 ? INT64_MIN : INT64_MAX;
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->on_comment = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
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) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
2021
- else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
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
- long before_len = method_name_beg - name_cstr;
2041
- long len = RSTRING_LEN(name) - before_len;
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 if (args->strict) {
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
- if (RB_UNLIKELY(args.unknown_keywords)) {
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
- if (Vsource == src) {
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
- long len;
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
- long len;
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
- long len;
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();