json 2.18.0 → 2.19.3

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.
@@ -7,8 +7,9 @@ static VALUE CNaN, CInfinity, CMinusInfinity;
7
7
 
8
8
  static ID i_new, i_try_convert, i_uminus, i_encode;
9
9
 
10
- static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters, sym_symbolize_names, sym_freeze,
11
- sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
10
+ static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters,
11
+ sym_allow_invalid_escape, sym_symbolize_names, sym_freeze, sym_decimal_class, sym_on_load,
12
+ sym_allow_duplicate_key;
12
13
 
13
14
  static int binary_encindex;
14
15
  static int utf8_encindex;
@@ -336,6 +337,7 @@ typedef struct JSON_ParserStruct {
336
337
  bool allow_nan;
337
338
  bool allow_trailing_comma;
338
339
  bool allow_control_characters;
340
+ bool allow_invalid_escape;
339
341
  bool symbolize_names;
340
342
  bool freeze;
341
343
  } JSON_ParserConfig;
@@ -400,14 +402,9 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
400
402
 
401
403
  #define PARSE_ERROR_FRAGMENT_LEN 32
402
404
 
403
- #ifdef RBIMPL_ATTR_NORETURN
404
- RBIMPL_ATTR_NORETURN()
405
- #endif
406
- static void raise_parse_error(const char *format, JSON_ParserState *state)
405
+ static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
407
406
  {
408
407
  unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
409
- long line, column;
410
- cursor_position(state, &line, &column);
411
408
 
412
409
  const char *ptr = "EOF";
413
410
  if (state->cursor && state->cursor < state->end) {
@@ -442,17 +439,26 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
442
439
  VALUE msg = rb_sprintf(format, ptr);
443
440
  VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
444
441
  RB_GC_GUARD(msg);
442
+ return message;
443
+ }
445
444
 
445
+ static VALUE parse_error_new(VALUE message, long line, long column)
446
+ {
446
447
  VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
447
448
  rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
448
449
  rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
449
- rb_exc_raise(exc);
450
+ return exc;
450
451
  }
451
452
 
452
- #ifdef RBIMPL_ATTR_NORETURN
453
- RBIMPL_ATTR_NORETURN()
454
- #endif
455
- static void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at)
453
+ NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
454
+ {
455
+ long line, column;
456
+ cursor_position(state, &line, &column);
457
+ VALUE message = build_parse_error_message(format, state, line, column);
458
+ rb_exc_raise(parse_error_new(message, line, column));
459
+ }
460
+
461
+ NORETURN(static) void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at)
456
462
  {
457
463
  state->cursor = at;
458
464
  raise_parse_error(format, state);
@@ -477,23 +483,24 @@ static const signed char digit_values[256] = {
477
483
  -1, -1, -1, -1, -1, -1, -1
478
484
  };
479
485
 
480
- static uint32_t unescape_unicode(JSON_ParserState *state, const unsigned char *p)
481
- {
482
- signed char b;
483
- uint32_t result = 0;
484
- b = digit_values[p[0]];
485
- if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2);
486
- result = (result << 4) | (unsigned char)b;
487
- b = digit_values[p[1]];
488
- if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2);
489
- result = (result << 4) | (unsigned char)b;
490
- b = digit_values[p[2]];
491
- if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2);
492
- result = (result << 4) | (unsigned char)b;
493
- b = digit_values[p[3]];
494
- if (b < 0) raise_parse_error_at("incomplete unicode character escape sequence at %s", state, (char *)p - 2);
495
- result = (result << 4) | (unsigned char)b;
496
- return result;
486
+ static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
487
+ {
488
+ if (RB_UNLIKELY(sp > spe - 4)) {
489
+ raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
490
+ }
491
+
492
+ const unsigned char *p = (const unsigned char *)sp;
493
+
494
+ const signed char b0 = digit_values[p[0]];
495
+ const signed char b1 = digit_values[p[1]];
496
+ const signed char b2 = digit_values[p[2]];
497
+ const signed char b3 = digit_values[p[3]];
498
+
499
+ if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) {
500
+ raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
501
+ }
502
+
503
+ return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3;
497
504
  }
498
505
 
499
506
  #define GET_PARSER_CONFIG \
@@ -643,7 +650,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserCon
643
650
  typedef struct _json_unescape_positions {
644
651
  long size;
645
652
  const char **positions;
646
- bool has_more;
653
+ unsigned long additional_backslashes;
647
654
  } JSON_UnescapePositions;
648
655
 
649
656
  static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
@@ -657,7 +664,8 @@ static inline const char *json_next_backslash(const char *pe, const char *string
657
664
  }
658
665
  }
659
666
 
660
- if (positions->has_more) {
667
+ if (positions->additional_backslashes) {
668
+ positions->additional_backslashes--;
661
669
  return memchr(pe, '\\', stringEnd - pe);
662
670
  }
663
671
 
@@ -707,50 +715,41 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
707
715
  case 'f':
708
716
  APPEND_CHAR('\f');
709
717
  break;
710
- case 'u':
711
- if (pe > stringEnd - 5) {
712
- raise_parse_error_at("incomplete unicode character escape sequence at %s", state, p);
713
- } else {
714
- uint32_t ch = unescape_unicode(state, (unsigned char *) ++pe);
715
- pe += 3;
716
- /* To handle values above U+FFFF, we take a sequence of
717
- * \uXXXX escapes in the U+D800..U+DBFF then
718
- * U+DC00..U+DFFF ranges, take the low 10 bits from each
719
- * to make a 20-bit number, then add 0x10000 to get the
720
- * final codepoint.
721
- *
722
- * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
723
- * Surrogate Pairs in UTF-16", and 23.6 "Surrogates
724
- * Area".
725
- */
726
- if ((ch & 0xFC00) == 0xD800) {
727
- pe++;
728
- if (pe > stringEnd - 6) {
729
- raise_parse_error_at("incomplete surrogate pair at %s", state, p);
730
- }
731
- if (pe[0] == '\\' && pe[1] == 'u') {
732
- uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2);
733
-
734
- if ((sur & 0xFC00) != 0xDC00) {
735
- raise_parse_error_at("invalid surrogate pair at %s", state, p);
736
- }
737
-
738
- ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
739
- | (sur & 0x3FF));
740
- pe += 5;
741
- } else {
742
- raise_parse_error_at("incomplete surrogate pair at %s", state, p);
743
- break;
718
+ case 'u': {
719
+ uint32_t ch = unescape_unicode(state, ++pe, stringEnd);
720
+ pe += 3;
721
+ /* To handle values above U+FFFF, we take a sequence of
722
+ * \uXXXX escapes in the U+D800..U+DBFF then
723
+ * U+DC00..U+DFFF ranges, take the low 10 bits from each
724
+ * to make a 20-bit number, then add 0x10000 to get the
725
+ * final codepoint.
726
+ *
727
+ * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
728
+ * Surrogate Pairs in UTF-16", and 23.6 "Surrogates
729
+ * Area".
730
+ */
731
+ if ((ch & 0xFC00) == 0xD800) {
732
+ pe++;
733
+ if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) {
734
+ uint32_t sur = unescape_unicode(state, pe + 2, stringEnd);
735
+
736
+ if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) {
737
+ raise_parse_error_at("invalid surrogate pair at %s", state, p);
744
738
  }
745
- }
746
739
 
747
- char buf[4];
748
- int unescape_len = convert_UTF32_to_UTF8(buf, ch);
749
- MEMCPY(buffer, buf, char, unescape_len);
750
- buffer += unescape_len;
751
- p = ++pe;
740
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF));
741
+ pe += 5;
742
+ } else {
743
+ raise_parse_error_at("incomplete surrogate pair at %s", state, p);
744
+ break;
745
+ }
752
746
  }
747
+
748
+ int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
749
+ buffer += unescape_len;
750
+ p = ++pe;
753
751
  break;
752
+ }
754
753
  default:
755
754
  if ((unsigned char)*pe < 0x20) {
756
755
  if (!config->allow_control_characters) {
@@ -759,6 +758,10 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
759
758
  }
760
759
  raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
761
760
  }
761
+ }
762
+
763
+ if (config->allow_invalid_escape) {
764
+ APPEND_CHAR(*pe);
762
765
  } else {
763
766
  raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
764
767
  }
@@ -783,20 +786,39 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
783
786
  }
784
787
 
785
788
  #define MAX_FAST_INTEGER_SIZE 18
789
+ #define MAX_NUMBER_STACK_BUFFER 128
790
+
791
+ typedef VALUE (*json_number_decode_func_t)(const char *ptr);
792
+
793
+ static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func)
794
+ {
795
+ if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
796
+ char buffer[MAX_NUMBER_STACK_BUFFER];
797
+ MEMCPY(buffer, start, char, len);
798
+ buffer[len] = '\0';
799
+ return func(buffer);
800
+ } else {
801
+ VALUE buffer_v = rb_str_tmp_new(len);
802
+ char *buffer = RSTRING_PTR(buffer_v);
803
+ MEMCPY(buffer, start, char, len);
804
+ buffer[len] = '\0';
805
+ VALUE number = func(buffer);
806
+ RB_GC_GUARD(buffer_v);
807
+ return number;
808
+ }
809
+ }
786
810
 
787
- static VALUE json_decode_large_integer(const char *start, long len)
811
+ static VALUE json_decode_inum(const char *buffer)
788
812
  {
789
- VALUE buffer_v;
790
- char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
791
- MEMCPY(buffer, start, char, len);
792
- buffer[len] = '\0';
793
- VALUE number = rb_cstr2inum(buffer, 10);
794
- RB_ALLOCV_END(buffer_v);
795
- return number;
813
+ return rb_cstr2inum(buffer, 10);
796
814
  }
797
815
 
798
- static inline VALUE
799
- json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
816
+ NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
817
+ {
818
+ return json_decode_large_number(start, len, json_decode_inum);
819
+ }
820
+
821
+ static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
800
822
  {
801
823
  if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) {
802
824
  if (negative) {
@@ -808,22 +830,14 @@ json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const
808
830
  return json_decode_large_integer(start, end - start);
809
831
  }
810
832
 
811
- static VALUE json_decode_large_float(const char *start, long len)
833
+ static VALUE json_decode_dnum(const char *buffer)
812
834
  {
813
- if (RB_LIKELY(len < 64)) {
814
- char buffer[64];
815
- MEMCPY(buffer, start, char, len);
816
- buffer[len] = '\0';
817
- return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
818
- }
835
+ return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
836
+ }
819
837
 
820
- VALUE buffer_v;
821
- char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
822
- MEMCPY(buffer, start, char, len);
823
- buffer[len] = '\0';
824
- VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
825
- RB_ALLOCV_END(buffer_v);
826
- return number;
838
+ NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
839
+ {
840
+ return json_decode_large_number(start, len, json_decode_dnum);
827
841
  }
828
842
 
829
843
  /* Ruby JSON optimized float decoder using vendored Ryu algorithm
@@ -875,7 +889,7 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
875
889
  return Qfalse;
876
890
  }
877
891
 
878
- static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
892
+ NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
879
893
  {
880
894
  VALUE message = rb_sprintf(
881
895
  "detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
@@ -886,16 +900,18 @@ static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_
886
900
  RB_GC_GUARD(message);
887
901
  }
888
902
 
889
- #ifdef RBIMPL_ATTR_NORETURN
890
- RBIMPL_ATTR_NORETURN()
891
- #endif
892
- static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
903
+ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
893
904
  {
894
905
  VALUE message = rb_sprintf(
895
906
  "duplicate key %"PRIsVALUE,
896
907
  rb_inspect(duplicate_key)
897
908
  );
898
909
 
910
+ long line, column;
911
+ cursor_position(state, &line, &column);
912
+ rb_str_concat(message, build_parse_error_message("", state, line, column)) ;
913
+ rb_exc_raise(parse_error_new(message, line, column));
914
+
899
915
  raise_parse_error(RSTRING_PTR(message), state);
900
916
  RB_GC_GUARD(message);
901
917
  }
@@ -992,7 +1008,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
992
1008
  JSON_UnescapePositions positions = {
993
1009
  .size = 0,
994
1010
  .positions = backslashes,
995
- .has_more = false,
1011
+ .additional_backslashes = 0,
996
1012
  };
997
1013
 
998
1014
  do {
@@ -1007,7 +1023,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
1007
1023
  backslashes[positions.size] = state->cursor;
1008
1024
  positions.size++;
1009
1025
  } else {
1010
- positions.has_more = true;
1026
+ positions.additional_backslashes++;
1011
1027
  }
1012
1028
  state->cursor++;
1013
1029
  break;
@@ -1440,6 +1456,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1440
1456
  else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
1441
1457
  else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
1442
1458
  else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
1459
+ else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
1443
1460
  else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
1444
1461
  else if (key == sym_freeze) { config->freeze = RTEST(val); }
1445
1462
  else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
@@ -1658,6 +1675,7 @@ void Init_parser(void)
1658
1675
  sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
1659
1676
  sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
1660
1677
  sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
1678
+ sym_allow_invalid_escape = ID2SYM(rb_intern("allow_invalid_escape"));
1661
1679
  sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
1662
1680
  sym_freeze = ID2SYM(rb_intern("freeze"));
1663
1681
  sym_on_load = ID2SYM(rb_intern("on_load"));
@@ -58,7 +58,34 @@ static inline int trailing_zeros(int input)
58
58
 
59
59
  #ifdef JSON_ENABLE_SIMD
60
60
 
61
- #define SIMD_MINIMUM_THRESHOLD 6
61
+ #define SIMD_MINIMUM_THRESHOLD 4
62
+
63
+ ALWAYS_INLINE(static) void json_fast_memcpy16(char *dst, const char *src, size_t len)
64
+ {
65
+ RBIMPL_ASSERT_OR_ASSUME(len < 16);
66
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); // 4
67
+ #if defined(__has_builtin) && __has_builtin(__builtin_memcpy)
68
+ // If __builtin_memcpy is available, use it to copy between SIMD_MINIMUM_THRESHOLD (4) and vec_len-1 (15) bytes.
69
+ // These copies overlap. The first copy will copy the first 8 (or 4) bytes. The second copy will copy
70
+ // the last 8 (or 4) bytes but overlap with the first copy. The overlapping bytes will be in the correct
71
+ // position in both copies.
72
+
73
+ // Please do not attempt to replace __builtin_memcpy with memcpy without profiling and/or looking at the
74
+ // generated assembly. On clang-specifically (tested on Apple clang version 17.0.0 (clang-1700.0.13.3)),
75
+ // when using memcpy, the compiler will notice the only difference is a 4 or 8 and generate a conditional
76
+ // select instruction instead of direct loads and stores with a branch. This ends up slower than the branch
77
+ // plus two loads and stores generated when using __builtin_memcpy.
78
+ if (len >= 8) {
79
+ __builtin_memcpy(dst, src, 8);
80
+ __builtin_memcpy(dst + len - 8, src + len - 8, 8);
81
+ } else {
82
+ __builtin_memcpy(dst, src, 4);
83
+ __builtin_memcpy(dst + len - 4, src + len - 4, 4);
84
+ }
85
+ #else
86
+ MEMCPY(dst, src, char, len);
87
+ #endif
88
+ }
62
89
 
63
90
  #if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64)
64
91
  #include <arm_neon.h>
@@ -106,16 +133,6 @@ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *en
106
133
  return 0;
107
134
  }
108
135
 
109
- static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
110
- {
111
- uint8x16x4_t tab;
112
- tab.val[0] = vld1q_u8(table);
113
- tab.val[1] = vld1q_u8(table+16);
114
- tab.val[2] = vld1q_u8(table+32);
115
- tab.val[3] = vld1q_u8(table+48);
116
- return tab;
117
- }
118
-
119
136
  #endif /* ARM Neon Support.*/
120
137
 
121
138
  #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
@@ -449,7 +449,7 @@ static int filter_special(double fp, char* dest)
449
449
  * }
450
450
  *
451
451
  */
452
- static int fpconv_dtoa(double d, char dest[28])
452
+ static int fpconv_dtoa(double d, char dest[32])
453
453
  {
454
454
  char digits[18];
455
455
 
data/lib/json/common.rb CHANGED
@@ -156,15 +156,17 @@ module JSON
156
156
  def generator=(generator) # :nodoc:
157
157
  old, $VERBOSE = $VERBOSE, nil
158
158
  @generator = generator
159
- generator_methods = generator::GeneratorMethods
160
- for const in generator_methods.constants
161
- klass = const_get(const)
162
- modul = generator_methods.const_get(const)
163
- klass.class_eval do
164
- instance_methods(false).each do |m|
165
- m.to_s == 'to_json' and remove_method m
159
+ if generator.const_defined?(:GeneratorMethods)
160
+ generator_methods = generator::GeneratorMethods
161
+ for const in generator_methods.constants
162
+ klass = const_get(const)
163
+ modul = generator_methods.const_get(const)
164
+ klass.class_eval do
165
+ instance_methods(false).each do |m|
166
+ m.to_s == 'to_json' and remove_method m
167
+ end
168
+ include modul
166
169
  end
167
- include modul
168
170
  end
169
171
  end
170
172
  self.state = generator::State
@@ -878,7 +880,7 @@ module JSON
878
880
  end
879
881
  end
880
882
 
881
- if opts[:allow_blank] && (source.nil? || source.empty?)
883
+ if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
882
884
  source = 'null'
883
885
  end
884
886
 
@@ -1036,7 +1038,8 @@ module JSON
1036
1038
  # JSON.new(options = nil, &block)
1037
1039
  #
1038
1040
  # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
1039
- # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
1041
+ # See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
1042
+ # and {Generating Options}[rdoc-ref:JSON@Generating+Options].
1040
1043
  #
1041
1044
  # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
1042
1045
  # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
@@ -1095,6 +1098,30 @@ module JSON
1095
1098
  load(File.read(path, encoding: Encoding::UTF_8))
1096
1099
  end
1097
1100
  end
1101
+
1102
+ module GeneratorMethods
1103
+ # call-seq: to_json(*)
1104
+ #
1105
+ # Converts this object into a JSON string.
1106
+ # If this object doesn't directly maps to a JSON native type,
1107
+ # first convert it to a string (calling #to_s), then converts
1108
+ # it to a JSON string, and returns the result.
1109
+ # This is a fallback, if no special method #to_json was defined for some object.
1110
+ def to_json(state = nil, *)
1111
+ obj = case self
1112
+ when nil, false, true, Integer, Float, Array, Hash
1113
+ self
1114
+ else
1115
+ "#{self}"
1116
+ end
1117
+
1118
+ if state.nil?
1119
+ JSON::State._generate_no_fallback(obj, nil, nil)
1120
+ else
1121
+ JSON::State.from_state(state)._generate_no_fallback(obj)
1122
+ end
1123
+ end
1124
+ end
1098
1125
  end
1099
1126
 
1100
1127
  module ::Kernel
@@ -1140,3 +1167,7 @@ module ::Kernel
1140
1167
  JSON[object, opts]
1141
1168
  end
1142
1169
  end
1170
+
1171
+ class Object
1172
+ include JSON::GeneratorMethods
1173
+ end
@@ -9,7 +9,7 @@ module JSON
9
9
  # Instantiates a new State object, configured by _opts_.
10
10
  #
11
11
  # Argument +opts+, if given, contains a \Hash of options for the generation.
12
- # See {Generating Options}[#module-JSON-label-Generating+Options].
12
+ # See {Generating Options}[rdoc-ref:JSON@Generating+Options].
13
13
  def initialize(opts = nil)
14
14
  if opts && !opts.empty?
15
15
  configure(opts)
@@ -211,7 +211,14 @@ module JSON
211
211
 
212
212
  # This integer returns the current depth data structure nesting in the
213
213
  # generated JSON.
214
- attr_accessor :depth
214
+ attr_reader :depth
215
+
216
+ def depth=(depth)
217
+ if depth.negative?
218
+ raise ArgumentError, "depth must be >= 0 (got #{depth})"
219
+ end
220
+ @depth = depth
221
+ end
215
222
 
216
223
  def check_max_nesting # :nodoc:
217
224
  return if @max_nesting.zero?
@@ -260,6 +267,11 @@ module JSON
260
267
  else
261
268
  raise TypeError, "can't convert #{opts.class} into Hash"
262
269
  end
270
+
271
+ if opts[:depth]&.negative?
272
+ raise ArgumentError, "depth must be >= 0 (got #{opts[:depth]})"
273
+ end
274
+
263
275
  opts.each do |key, value|
264
276
  instance_variable_set "@#{key}", value
265
277
  end
data/lib/json/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.18.0'
4
+ VERSION = '2.19.3'
5
5
  end
data/lib/json.rb CHANGED
@@ -6,6 +6,15 @@ require 'json/common'
6
6
  #
7
7
  # \JSON is a lightweight data-interchange format.
8
8
  #
9
+ # \JSON is easy for us humans to read and write,
10
+ # and equally simple for machines to read (parse) and write (generate).
11
+ #
12
+ # \JSON is language-independent, making it an ideal interchange format
13
+ # for applications in differing programming languages
14
+ # and on differing operating systems.
15
+ #
16
+ # == \JSON Values
17
+ #
9
18
  # A \JSON value is one of the following:
10
19
  # - Double-quoted text: <tt>"foo"</tt>.
11
20
  # - Number: +1+, +1.0+, +2.0e2+.
@@ -173,6 +182,30 @@ require 'json/common'
173
182
  # When enabled:
174
183
  # JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
175
184
  #
185
+ # ---
186
+ #
187
+ # Option +allow_control_characters+ (boolean) specifies whether to allow
188
+ # unescaped ASCII control characters, such as newlines, in strings;
189
+ # defaults to +false+.
190
+ #
191
+ # With the default, +false+:
192
+ # JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)
193
+ #
194
+ # When enabled:
195
+ # JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"
196
+ #
197
+ # ---
198
+ #
199
+ # Option +allow_invalid_escape+ (boolean) specifies whether to ignore backslahes that are followed
200
+ # by an invalid escape character in strings;
201
+ # defaults to +false+.
202
+ #
203
+ # With the default, +false+:
204
+ # JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)
205
+ #
206
+ # When enabled:
207
+ # JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
208
+ #
176
209
  # ====== Output Options
177
210
  #
178
211
  # Option +freeze+ (boolean) specifies whether the returned objects will be frozen;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.18.0
4
+ version: 2.19.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 3.6.9
87
+ rubygems_version: 4.0.3
88
88
  specification_version: 4
89
89
  summary: JSON Implementation for Ruby
90
90
  test_files: []