json 2.16.0 → 2.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8bd00bbc63367659eb90ee7e3f4f1394fecdf713fc724f7908c6a2c7b79bbf7
4
- data.tar.gz: efa1d81f687277247649de777f25be01e56ad8dfac1bdd9ce61111e10231185e
3
+ metadata.gz: a35454680e80a622fb539155325b2f693e90b352aad3f24b7f9eafc5ca797963
4
+ data.tar.gz: 9eee4f7ce5348a1eb44100766d5a7b326a4e76691fbfd3db8a2410020fb75ec1
5
5
  SHA512:
6
- metadata.gz: cb8b505c08b88115a87aa0b6ab8b7f34d6e0611af7d37277cef243131990db6cb77ca2508045104588607d54bd58b0c2e67ce0728bd72c4c32d2d77a56b9762e
7
- data.tar.gz: 8dec0d5ba99f33e68049328f060a600da2fe0545caf47c44c2e812a8316cbe64a99152f365be8654cb4af45493de3bed97baa61a0b2a71ae71a7466d0ee11e8c
6
+ metadata.gz: 0771fad39331fe9d47dbd855a02f662053a79ff08679c8bc76855dcfd3edbcdb32746cc0db435e6ea14815eacdbe97bf1152b704eacb7ddf1423917eb2545923
7
+ data.tar.gz: 742dc87f4cc0306e613cfb65824aaee600aa53f0715135190ffaa4ca1f25aa6820a10b1860befcf90f3dcf6676bd5bb304f0e2858ec1e5933f66b089fca82e78
data/CHANGES.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2025-12-03 (2.17.0)
6
+
7
+ * Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
8
+ * Fix the parser to no longer ignore invalid escapes in strings.
9
+ Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
10
+ * Fixed `JSON::Coder` to use the depth it was initialized with.
11
+ * On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
12
+ * Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
13
+ automatically to its initial value.
14
+ In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
15
+ generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
16
+
5
17
  ### 2025-11-07 (2.16.0)
6
18
 
7
19
  * Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
@@ -14,7 +14,7 @@ typedef struct FBufferStruct {
14
14
  unsigned long initial_length;
15
15
  unsigned long len;
16
16
  unsigned long capa;
17
- #ifdef JSON_DEBUG
17
+ #if JSON_DEBUG
18
18
  unsigned long requested;
19
19
  #endif
20
20
  char *ptr;
@@ -45,14 +45,14 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
45
45
  fb->ptr = stack_buffer;
46
46
  fb->capa = stack_buffer_size;
47
47
  }
48
- #ifdef JSON_DEBUG
48
+ #if JSON_DEBUG
49
49
  fb->requested = 0;
50
50
  #endif
51
51
  }
52
52
 
53
53
  static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
54
54
  {
55
- #ifdef JSON_DEBUG
55
+ #if JSON_DEBUG
56
56
  if (consumed > fb->requested) {
57
57
  rb_bug("fbuffer: Out of bound write");
58
58
  }
@@ -122,7 +122,7 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
122
122
 
123
123
  static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
124
124
  {
125
- #ifdef JSON_DEBUG
125
+ #if JSON_DEBUG
126
126
  fb->requested = requested;
127
127
  #endif
128
128
 
@@ -148,7 +148,7 @@ static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long
148
148
  /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
149
149
  static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
150
150
  {
151
- #ifdef JSON_DEBUG
151
+ #if JSON_DEBUG
152
152
  if (fb->requested < 1) {
153
153
  rb_bug("fbuffer: unreserved write");
154
154
  }
@@ -174,7 +174,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
174
174
 
175
175
  fbuffer_inc_capa(fb, repeat * len);
176
176
  while (repeat) {
177
- #ifdef JSON_DEBUG
177
+ #if JSON_DEBUG
178
178
  fb->requested = len;
179
179
  #endif
180
180
  fbuffer_append_reserved(fb, newstr, len);
@@ -6,7 +6,7 @@ if RUBY_ENGINE == 'truffleruby'
6
6
  else
7
7
  append_cflags("-std=c99")
8
8
  $defs << "-DJSON_GENERATOR"
9
- $defs << "-DJSON_DEBUG" if ENV["JSON_DEBUG"]
9
+ $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
10
10
 
11
11
  if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
12
12
  load __dir__ + "/../simd/conf.rb"
@@ -38,7 +38,7 @@ typedef struct JSON_Generator_StateStruct {
38
38
 
39
39
  static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
40
40
 
41
- static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
41
+ static ID i_to_s, i_to_json, i_new, i_encode;
42
42
  static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
43
43
  sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
44
44
 
@@ -60,6 +60,7 @@ struct generate_json_data {
60
60
  JSON_Generator_State *state;
61
61
  VALUE obj;
62
62
  generator_func func;
63
+ long depth;
63
64
  };
64
65
 
65
66
  static VALUE cState_from_state_s(VALUE self, VALUE opts);
@@ -127,7 +128,7 @@ typedef struct _search_state {
127
128
  #endif /* HAVE_SIMD */
128
129
  } search_state;
129
130
 
130
- static ALWAYS_INLINE() void search_flush(search_state *search)
131
+ ALWAYS_INLINE(static) void search_flush(search_state *search)
131
132
  {
132
133
  // Do not remove this conditional without profiling, specifically escape-heavy text.
133
134
  // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
@@ -170,7 +171,7 @@ static inline unsigned char search_escape_basic(search_state *search)
170
171
  return 0;
171
172
  }
172
173
 
173
- static ALWAYS_INLINE() void escape_UTF8_char_basic(search_state *search)
174
+ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
174
175
  {
175
176
  const unsigned char ch = (unsigned char)*search->ptr;
176
177
  switch (ch) {
@@ -257,7 +258,7 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
257
258
 
258
259
  #ifdef HAVE_SIMD
259
260
 
260
- static ALWAYS_INLINE() char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
261
+ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
261
262
  {
262
263
  // Flush the buffer so everything up until the last 'len' characters are unflushed.
263
264
  search_flush(search);
@@ -280,7 +281,7 @@ static ALWAYS_INLINE() char *copy_remaining_bytes(search_state *search, unsigned
280
281
 
281
282
  #ifdef HAVE_SIMD_NEON
282
283
 
283
- static ALWAYS_INLINE() unsigned char neon_next_match(search_state *search)
284
+ ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
284
285
  {
285
286
  uint64_t mask = search->matches_mask;
286
287
  uint32_t index = trailing_zeros64(mask) >> 2;
@@ -394,7 +395,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
394
395
 
395
396
  #ifdef HAVE_SIMD_SSE2
396
397
 
397
- static ALWAYS_INLINE() unsigned char sse2_next_match(search_state *search)
398
+ ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
398
399
  {
399
400
  int mask = search->matches_mask;
400
401
  int index = trailing_zeros(mask);
@@ -418,7 +419,7 @@ static ALWAYS_INLINE() unsigned char sse2_next_match(search_state *search)
418
419
  #define TARGET_SSE2
419
420
  #endif
420
421
 
421
- static TARGET_SSE2 ALWAYS_INLINE() unsigned char search_escape_basic_sse2(search_state *search)
422
+ ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
422
423
  {
423
424
  if (RB_UNLIKELY(search->has_matches)) {
424
425
  // There are more matches if search->matches_mask > 0.
@@ -926,11 +927,6 @@ static size_t State_memsize(const void *ptr)
926
927
  return sizeof(JSON_Generator_State);
927
928
  }
928
929
 
929
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
930
- # undef RUBY_TYPED_FROZEN_SHAREABLE
931
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
932
- #endif
933
-
934
930
  static const rb_data_type_t JSON_Generator_State_type = {
935
931
  "JSON/Generator/State",
936
932
  {
@@ -972,12 +968,16 @@ static void vstate_spill(struct generate_json_data *data)
972
968
  RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
973
969
  }
974
970
 
975
- static inline VALUE vstate_get(struct generate_json_data *data)
971
+ static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
976
972
  {
977
973
  if (RB_UNLIKELY(!data->vstate)) {
978
974
  vstate_spill(data);
979
975
  }
980
- return data->vstate;
976
+ GET_STATE(data->vstate);
977
+ state->depth = data->depth;
978
+ VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
979
+ // no need to restore state->depth, vstate is just a temporary State
980
+ return tmp;
981
981
  }
982
982
 
983
983
  static VALUE
@@ -1125,8 +1125,7 @@ struct hash_foreach_arg {
1125
1125
  bool mixed_keys_encountered;
1126
1126
  };
1127
1127
 
1128
- NOINLINE()
1129
- static void
1128
+ NOINLINE(static) void
1130
1129
  json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
1131
1130
  {
1132
1131
  if (arg->mixed_keys_encountered) {
@@ -1150,7 +1149,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1150
1149
  FBuffer *buffer = data->buffer;
1151
1150
  JSON_Generator_State *state = data->state;
1152
1151
 
1153
- long depth = state->depth;
1152
+ long depth = data->depth;
1154
1153
  int key_type = rb_type(key);
1155
1154
 
1156
1155
  if (arg->first) {
@@ -1224,9 +1223,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
1224
1223
  static inline long increase_depth(struct generate_json_data *data)
1225
1224
  {
1226
1225
  JSON_Generator_State *state = data->state;
1227
- long depth = ++state->depth;
1226
+ long depth = ++data->depth;
1228
1227
  if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
1229
- rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --state->depth);
1228
+ rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
1230
1229
  }
1231
1230
  return depth;
1232
1231
  }
@@ -1237,7 +1236,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
1237
1236
 
1238
1237
  if (RHASH_SIZE(obj) == 0) {
1239
1238
  fbuffer_append(buffer, "{}", 2);
1240
- --data->state->depth;
1239
+ --data->depth;
1241
1240
  return;
1242
1241
  }
1243
1242
 
@@ -1250,7 +1249,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
1250
1249
  };
1251
1250
  rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
1252
1251
 
1253
- depth = --data->state->depth;
1252
+ depth = --data->depth;
1254
1253
  if (RB_UNLIKELY(data->state->object_nl)) {
1255
1254
  fbuffer_append_str(buffer, data->state->object_nl);
1256
1255
  if (RB_UNLIKELY(data->state->indent)) {
@@ -1266,7 +1265,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
1266
1265
 
1267
1266
  if (RARRAY_LEN(obj) == 0) {
1268
1267
  fbuffer_append(buffer, "[]", 2);
1269
- --data->state->depth;
1268
+ --data->depth;
1270
1269
  return;
1271
1270
  }
1272
1271
 
@@ -1282,7 +1281,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
1282
1281
  }
1283
1282
  generate_json(buffer, data, RARRAY_AREF(obj, i));
1284
1283
  }
1285
- data->state->depth = --depth;
1284
+ data->depth = --depth;
1286
1285
  if (RB_UNLIKELY(data->state->array_nl)) {
1287
1286
  fbuffer_append_str(buffer, data->state->array_nl);
1288
1287
  if (RB_UNLIKELY(data->state->indent)) {
@@ -1296,7 +1295,7 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
1296
1295
  {
1297
1296
  VALUE tmp;
1298
1297
  if (rb_respond_to(obj, i_to_json)) {
1299
- tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
1298
+ tmp = json_call_to_json(data, obj);
1300
1299
  Check_Type(tmp, T_STRING);
1301
1300
  fbuffer_append_str(buffer, tmp);
1302
1301
  } else {
@@ -1363,7 +1362,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
1363
1362
  if (casted_obj != obj) {
1364
1363
  increase_depth(data);
1365
1364
  generate_json(buffer, data, casted_obj);
1366
- data->state->depth--;
1365
+ data->depth--;
1367
1366
  return;
1368
1367
  }
1369
1368
  }
@@ -1498,8 +1497,9 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
1498
1497
 
1499
1498
  struct generate_json_data data = {
1500
1499
  .buffer = &buffer,
1501
- .vstate = self,
1500
+ .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
1502
1501
  .state = state,
1502
+ .depth = state->depth,
1503
1503
  .obj = obj,
1504
1504
  .func = func
1505
1505
  };
@@ -1522,36 +1522,6 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
1522
1522
  return cState_partial_generate(self, obj, generate_json, io);
1523
1523
  }
1524
1524
 
1525
- static VALUE cState_generate_new(int argc, VALUE *argv, VALUE self)
1526
- {
1527
- rb_check_arity(argc, 1, 2);
1528
- VALUE obj = argv[0];
1529
- VALUE io = argc > 1 ? argv[1] : Qnil;
1530
-
1531
- GET_STATE(self);
1532
-
1533
- JSON_Generator_State new_state;
1534
- MEMCPY(&new_state, state, JSON_Generator_State, 1);
1535
-
1536
- // FIXME: depth shouldn't be part of JSON_Generator_State, as that prevents it from being used concurrently.
1537
- new_state.depth = 0;
1538
-
1539
- char stack_buffer[FBUFFER_STACK_SIZE];
1540
- FBuffer buffer = {
1541
- .io = RTEST(io) ? io : Qfalse,
1542
- };
1543
- fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1544
-
1545
- struct generate_json_data data = {
1546
- .buffer = &buffer,
1547
- .vstate = Qfalse,
1548
- .state = &new_state,
1549
- .obj = obj,
1550
- .func = generate_json
1551
- };
1552
- return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
1553
- }
1554
-
1555
1525
  static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
1556
1526
  {
1557
1527
  rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
@@ -1630,6 +1600,7 @@ static VALUE string_config(VALUE config)
1630
1600
  */
1631
1601
  static VALUE cState_indent_set(VALUE self, VALUE indent)
1632
1602
  {
1603
+ rb_check_frozen(self);
1633
1604
  GET_STATE(self);
1634
1605
  RB_OBJ_WRITE(self, &state->indent, string_config(indent));
1635
1606
  return Qnil;
@@ -1655,6 +1626,7 @@ static VALUE cState_space(VALUE self)
1655
1626
  */
1656
1627
  static VALUE cState_space_set(VALUE self, VALUE space)
1657
1628
  {
1629
+ rb_check_frozen(self);
1658
1630
  GET_STATE(self);
1659
1631
  RB_OBJ_WRITE(self, &state->space, string_config(space));
1660
1632
  return Qnil;
@@ -1678,6 +1650,7 @@ static VALUE cState_space_before(VALUE self)
1678
1650
  */
1679
1651
  static VALUE cState_space_before_set(VALUE self, VALUE space_before)
1680
1652
  {
1653
+ rb_check_frozen(self);
1681
1654
  GET_STATE(self);
1682
1655
  RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
1683
1656
  return Qnil;
@@ -1703,6 +1676,7 @@ static VALUE cState_object_nl(VALUE self)
1703
1676
  */
1704
1677
  static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
1705
1678
  {
1679
+ rb_check_frozen(self);
1706
1680
  GET_STATE(self);
1707
1681
  RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
1708
1682
  return Qnil;
@@ -1726,6 +1700,7 @@ static VALUE cState_array_nl(VALUE self)
1726
1700
  */
1727
1701
  static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
1728
1702
  {
1703
+ rb_check_frozen(self);
1729
1704
  GET_STATE(self);
1730
1705
  RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
1731
1706
  return Qnil;
@@ -1749,6 +1724,7 @@ static VALUE cState_as_json(VALUE self)
1749
1724
  */
1750
1725
  static VALUE cState_as_json_set(VALUE self, VALUE as_json)
1751
1726
  {
1727
+ rb_check_frozen(self);
1752
1728
  GET_STATE(self);
1753
1729
  RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
1754
1730
  return Qnil;
@@ -1791,6 +1767,7 @@ static long long_config(VALUE num)
1791
1767
  */
1792
1768
  static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
1793
1769
  {
1770
+ rb_check_frozen(self);
1794
1771
  GET_STATE(self);
1795
1772
  state->max_nesting = long_config(depth);
1796
1773
  return Qnil;
@@ -1816,6 +1793,7 @@ static VALUE cState_script_safe(VALUE self)
1816
1793
  */
1817
1794
  static VALUE cState_script_safe_set(VALUE self, VALUE enable)
1818
1795
  {
1796
+ rb_check_frozen(self);
1819
1797
  GET_STATE(self);
1820
1798
  state->script_safe = RTEST(enable);
1821
1799
  return Qnil;
@@ -1847,6 +1825,7 @@ static VALUE cState_strict(VALUE self)
1847
1825
  */
1848
1826
  static VALUE cState_strict_set(VALUE self, VALUE enable)
1849
1827
  {
1828
+ rb_check_frozen(self);
1850
1829
  GET_STATE(self);
1851
1830
  state->strict = RTEST(enable);
1852
1831
  return Qnil;
@@ -1871,6 +1850,7 @@ static VALUE cState_allow_nan_p(VALUE self)
1871
1850
  */
1872
1851
  static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
1873
1852
  {
1853
+ rb_check_frozen(self);
1874
1854
  GET_STATE(self);
1875
1855
  state->allow_nan = RTEST(enable);
1876
1856
  return Qnil;
@@ -1895,6 +1875,7 @@ static VALUE cState_ascii_only_p(VALUE self)
1895
1875
  */
1896
1876
  static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
1897
1877
  {
1878
+ rb_check_frozen(self);
1898
1879
  GET_STATE(self);
1899
1880
  state->ascii_only = RTEST(enable);
1900
1881
  return Qnil;
@@ -1932,6 +1913,7 @@ static VALUE cState_depth(VALUE self)
1932
1913
  */
1933
1914
  static VALUE cState_depth_set(VALUE self, VALUE depth)
1934
1915
  {
1916
+ rb_check_frozen(self);
1935
1917
  GET_STATE(self);
1936
1918
  state->depth = long_config(depth);
1937
1919
  return Qnil;
@@ -1965,6 +1947,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
1965
1947
  */
1966
1948
  static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
1967
1949
  {
1950
+ rb_check_frozen(self);
1968
1951
  GET_STATE(self);
1969
1952
  buffer_initial_length_set(state, buffer_initial_length);
1970
1953
  return Qnil;
@@ -2031,6 +2014,7 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
2031
2014
 
2032
2015
  static VALUE cState_configure(VALUE self, VALUE opts)
2033
2016
  {
2017
+ rb_check_frozen(self);
2034
2018
  GET_STATE(self);
2035
2019
  configure_state(state, self, opts);
2036
2020
  return self;
@@ -2052,6 +2036,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
2052
2036
  .buffer = &buffer,
2053
2037
  .vstate = Qfalse,
2054
2038
  .state = &state,
2039
+ .depth = state.depth,
2055
2040
  .obj = obj,
2056
2041
  .func = generate_json,
2057
2042
  };
@@ -2125,7 +2110,6 @@ void Init_generator(void)
2125
2110
  rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
2126
2111
  rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
2127
2112
  rb_define_method(cState, "generate", cState_generate, -1);
2128
- rb_define_method(cState, "generate_new", cState_generate_new, -1); // :nodoc:
2129
2113
 
2130
2114
  rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
2131
2115
 
@@ -2173,10 +2157,6 @@ void Init_generator(void)
2173
2157
  i_to_s = rb_intern("to_s");
2174
2158
  i_to_json = rb_intern("to_json");
2175
2159
  i_new = rb_intern("new");
2176
- i_pack = rb_intern("pack");
2177
- i_unpack = rb_intern("unpack");
2178
- i_create_id = rb_intern("create_id");
2179
- i_extend = rb_intern("extend");
2180
2160
  i_encode = rb_intern("encode");
2181
2161
 
2182
2162
  sym_indent = ID2SYM(rb_intern("indent"));
data/ext/json/ext/json.h CHANGED
@@ -45,6 +45,11 @@ typedef unsigned char _Bool;
45
45
  #endif
46
46
  #endif
47
47
 
48
+ #ifndef HAVE_RB_EXT_RACTOR_SAFE
49
+ # undef RUBY_TYPED_FROZEN_SHAREABLE
50
+ # define RUBY_TYPED_FROZEN_SHAREABLE 0
51
+ #endif
52
+
48
53
  #ifndef NORETURN
49
54
  #define NORETURN(x) x
50
55
  #endif
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  require 'mkmf'
3
3
 
4
- $defs << "-DJSON_DEBUG" if ENV["JSON_DEBUG"]
4
+ $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
5
5
  have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
6
6
  have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
7
7
  have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
8
8
  have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
9
- have_func("strnlen", "string.h") # Missing on Solaris 10
10
9
 
11
10
  append_cflags("-std=c99")
12
11
 
@@ -5,8 +5,7 @@
5
5
  static VALUE mJSON, eNestingError, Encoding_UTF_8;
6
6
  static VALUE CNaN, CInfinity, CMinusInfinity;
7
7
 
8
- static ID i_chr, i_aset, i_aref,
9
- i_leftshift, i_new, i_try_convert, i_uminus, i_encode;
8
+ static ID i_new, i_try_convert, i_uminus, i_encode;
10
9
 
11
10
  static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
12
11
  sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
@@ -89,7 +88,7 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring
89
88
  #if JSON_CPU_LITTLE_ENDIAN_64BITS
90
89
  #if __has_builtin(__builtin_bswap64)
91
90
  #undef rstring_cache_memcmp
92
- static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length)
91
+ ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length)
93
92
  {
94
93
  // The libc memcmp has numerous complex optimizations, but in this particular case,
95
94
  // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to
@@ -118,7 +117,7 @@ static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rpt
118
117
  #endif
119
118
  #endif
120
119
 
121
- static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
120
+ ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
122
121
  {
123
122
  const char *rstring_ptr;
124
123
  long rstring_length;
@@ -132,7 +131,7 @@ static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length,
132
131
  }
133
132
  }
134
133
 
135
- static ALWAYS_INLINE() VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
134
+ ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
136
135
  {
137
136
  int low = 0;
138
137
  int high = cache->length - 1;
@@ -296,15 +295,6 @@ static void rvalue_stack_eagerly_release(VALUE handle)
296
295
  }
297
296
  }
298
297
 
299
-
300
- #ifndef HAVE_STRNLEN
301
- static size_t strnlen(const char *s, size_t maxlen)
302
- {
303
- char *p;
304
- return ((p = memchr(s, '\0', maxlen)) ? p - s : maxlen);
305
- }
306
- #endif
307
-
308
298
  static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
309
299
  {
310
300
  int len = 1;
@@ -345,7 +335,6 @@ typedef struct JSON_ParserStruct {
345
335
  int max_nesting;
346
336
  bool allow_nan;
347
337
  bool allow_trailing_comma;
348
- bool parsing_name;
349
338
  bool symbolize_names;
350
339
  bool freeze;
351
340
  } JSON_ParserConfig;
@@ -551,7 +540,7 @@ json_eat_comments(JSON_ParserState *state)
551
540
  }
552
541
  }
553
542
 
554
- static ALWAYS_INLINE() void
543
+ ALWAYS_INLINE(static) void
555
544
  json_eat_whitespace(JSON_ParserState *state)
556
545
  {
557
546
  while (true) {
@@ -627,8 +616,10 @@ static inline bool json_string_cacheable_p(const char *string, size_t length)
627
616
  return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]);
628
617
  }
629
618
 
630
- static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *string, const char *stringEnd, bool is_name, bool intern, bool symbolize)
619
+ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name)
631
620
  {
621
+ bool intern = is_name || config->freeze;
622
+ bool symbolize = is_name && config->symbolize_names;
632
623
  size_t bufferSize = stringEnd - string;
633
624
 
634
625
  if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) {
@@ -647,47 +638,71 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *st
647
638
  return build_string(string, stringEnd, intern, symbolize);
648
639
  }
649
640
 
650
- static VALUE json_string_unescape(JSON_ParserState *state, const char *string, const char *stringEnd, bool is_name, bool intern, bool symbolize)
641
+ #define JSON_MAX_UNESCAPE_POSITIONS 16
642
+ typedef struct _json_unescape_positions {
643
+ long size;
644
+ const char **positions;
645
+ bool has_more;
646
+ } JSON_UnescapePositions;
647
+
648
+ static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
649
+ {
650
+ while (positions->size) {
651
+ positions->size--;
652
+ const char *next_position = positions->positions[0];
653
+ positions->positions++;
654
+ return next_position;
655
+ }
656
+
657
+ if (positions->has_more) {
658
+ return memchr(pe, '\\', stringEnd - pe);
659
+ }
660
+
661
+ return NULL;
662
+ }
663
+
664
+ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions)
651
665
  {
666
+ bool intern = is_name || config->freeze;
667
+ bool symbolize = is_name && config->symbolize_names;
652
668
  size_t bufferSize = stringEnd - string;
653
- const char *p = string, *pe = string, *unescape, *bufferStart;
669
+ const char *p = string, *pe = string, *bufferStart;
654
670
  char *buffer;
655
- int unescape_len;
656
- char buf[4];
657
671
 
658
672
  VALUE result = rb_str_buf_new(bufferSize);
659
673
  rb_enc_associate_index(result, utf8_encindex);
660
674
  buffer = RSTRING_PTR(result);
661
675
  bufferStart = buffer;
662
676
 
663
- while (pe < stringEnd && (pe = memchr(pe, '\\', stringEnd - pe))) {
664
- unescape = (char *) "?";
665
- unescape_len = 1;
677
+ #define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe;
678
+
679
+ while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) {
666
680
  if (pe > p) {
667
681
  MEMCPY(buffer, p, char, pe - p);
668
682
  buffer += pe - p;
669
683
  }
670
684
  switch (*++pe) {
685
+ case '"':
686
+ case '/':
687
+ p = pe; // nothing to unescape just need to skip the backslash
688
+ break;
689
+ case '\\':
690
+ APPEND_CHAR('\\');
691
+ break;
671
692
  case 'n':
672
- unescape = (char *) "\n";
693
+ APPEND_CHAR('\n');
673
694
  break;
674
695
  case 'r':
675
- unescape = (char *) "\r";
696
+ APPEND_CHAR('\r');
676
697
  break;
677
698
  case 't':
678
- unescape = (char *) "\t";
679
- break;
680
- case '"':
681
- unescape = (char *) "\"";
682
- break;
683
- case '\\':
684
- unescape = (char *) "\\";
699
+ APPEND_CHAR('\t');
685
700
  break;
686
701
  case 'b':
687
- unescape = (char *) "\b";
702
+ APPEND_CHAR('\b');
688
703
  break;
689
704
  case 'f':
690
- unescape = (char *) "\f";
705
+ APPEND_CHAR('\f');
691
706
  break;
692
707
  case 'u':
693
708
  if (pe > stringEnd - 5) {
@@ -725,18 +740,23 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
725
740
  break;
726
741
  }
727
742
  }
728
- unescape_len = convert_UTF32_to_UTF8(buf, ch);
729
- unescape = buf;
743
+
744
+ char buf[4];
745
+ int unescape_len = convert_UTF32_to_UTF8(buf, ch);
746
+ MEMCPY(buffer, buf, char, unescape_len);
747
+ buffer += unescape_len;
748
+ p = ++pe;
730
749
  }
731
750
  break;
732
751
  default:
733
- p = pe;
734
- continue;
752
+ if ((unsigned char)*pe < 0x20) {
753
+ raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
754
+ }
755
+ raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
756
+ break;
735
757
  }
736
- MEMCPY(buffer, unescape, char, unescape_len);
737
- buffer += unescape_len;
738
- p = ++pe;
739
758
  }
759
+ #undef APPEND_CHAR
740
760
 
741
761
  if (stringEnd > p) {
742
762
  MEMCPY(buffer, p, char, stringEnd - p);
@@ -900,20 +920,6 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
900
920
  return object;
901
921
  }
902
922
 
903
- static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfig *config, const char *start, const char *end, bool escaped, bool is_name)
904
- {
905
- VALUE string;
906
- bool intern = is_name || config->freeze;
907
- bool symbolize = is_name && config->symbolize_names;
908
- if (escaped) {
909
- string = json_string_unescape(state, start, end, is_name, intern, symbolize);
910
- } else {
911
- string = json_string_fastpath(state, start, end, is_name, intern, symbolize);
912
- }
913
-
914
- return string;
915
- }
916
-
917
923
  static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
918
924
  {
919
925
  if (RB_UNLIKELY(config->on_load_proc)) {
@@ -940,7 +946,7 @@ static const bool string_scan_table[256] = {
940
946
  static SIMD_Implementation simd_impl = SIMD_NONE;
941
947
  #endif /* HAVE_SIMD */
942
948
 
943
- static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
949
+ ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
944
950
  {
945
951
  #ifdef HAVE_SIMD
946
952
  #if defined(HAVE_SIMD_NEON)
@@ -948,7 +954,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
948
954
  uint64_t mask = 0;
949
955
  if (string_scan_simd_neon(&state->cursor, state->end, &mask)) {
950
956
  state->cursor += trailing_zeros64(mask) >> 2;
951
- return 1;
957
+ return true;
952
958
  }
953
959
 
954
960
  #elif defined(HAVE_SIMD_SSE2)
@@ -956,7 +962,7 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
956
962
  int mask = 0;
957
963
  if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) {
958
964
  state->cursor += trailing_zeros(mask);
959
- return 1;
965
+ return true;
960
966
  }
961
967
  }
962
968
  #endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */
@@ -964,32 +970,37 @@ static ALWAYS_INLINE() bool string_scan(JSON_ParserState *state)
964
970
 
965
971
  while (!eos(state)) {
966
972
  if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
967
- return 1;
973
+ return true;
968
974
  }
969
975
  state->cursor++;
970
976
  }
971
- return 0;
977
+ return false;
972
978
  }
973
979
 
974
- static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name)
980
+ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start)
975
981
  {
976
- state->cursor++;
977
- const char *start = state->cursor;
978
- bool escaped = false;
982
+ const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS];
983
+ JSON_UnescapePositions positions = {
984
+ .size = 0,
985
+ .positions = backslashes,
986
+ .has_more = false,
987
+ };
979
988
 
980
- while (RB_UNLIKELY(string_scan(state))) {
989
+ do {
981
990
  switch (*state->cursor) {
982
991
  case '"': {
983
- VALUE string = json_decode_string(state, config, start, state->cursor, escaped, is_name);
992
+ VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions);
984
993
  state->cursor++;
985
994
  return json_push_value(state, config, string);
986
995
  }
987
996
  case '\\': {
988
- state->cursor++;
989
- escaped = true;
990
- if ((unsigned char)*state->cursor < 0x20) {
991
- raise_parse_error("invalid ASCII control character in string: %s", state);
997
+ if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) {
998
+ backslashes[positions.size] = state->cursor;
999
+ positions.size++;
1000
+ } else {
1001
+ positions.has_more = true;
992
1002
  }
1003
+ state->cursor++;
993
1004
  break;
994
1005
  }
995
1006
  default:
@@ -998,12 +1009,29 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
998
1009
  }
999
1010
 
1000
1011
  state->cursor++;
1001
- }
1012
+ } while (string_scan(state));
1002
1013
 
1003
1014
  raise_parse_error("unexpected end of input, expected closing \"", state);
1004
1015
  return Qfalse;
1005
1016
  }
1006
1017
 
1018
+ ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name)
1019
+ {
1020
+ state->cursor++;
1021
+ const char *start = state->cursor;
1022
+
1023
+ if (RB_UNLIKELY(!string_scan(state))) {
1024
+ raise_parse_error("unexpected end of input, expected closing \"", state);
1025
+ }
1026
+
1027
+ if (RB_LIKELY(*state->cursor == '"')) {
1028
+ VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name);
1029
+ state->cursor++;
1030
+ return json_push_value(state, config, string);
1031
+ }
1032
+ return json_parse_escaped_string(state, config, is_name, start);
1033
+ }
1034
+
1007
1035
  #if JSON_CPU_LITTLE_ENDIAN_64BITS
1008
1036
  // From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/
1009
1037
  // Additional References:
@@ -1477,6 +1505,7 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
1477
1505
  */
1478
1506
  static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
1479
1507
  {
1508
+ rb_check_frozen(self);
1480
1509
  GET_PARSER_CONFIG;
1481
1510
 
1482
1511
  parser_config_init(config, opts);
@@ -1572,7 +1601,7 @@ static const rb_data_type_t JSON_ParserConfig_type = {
1572
1601
  JSON_ParserConfig_memsize,
1573
1602
  },
1574
1603
  0, 0,
1575
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1604
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
1576
1605
  };
1577
1606
 
1578
1607
  static VALUE cJSON_parser_s_allocate(VALUE klass)
@@ -1622,10 +1651,6 @@ void Init_parser(void)
1622
1651
  sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
1623
1652
  sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
1624
1653
 
1625
- i_chr = rb_intern("chr");
1626
- i_aset = rb_intern("[]=");
1627
- i_aref = rb_intern("[]");
1628
- i_leftshift = rb_intern("<<");
1629
1654
  i_new = rb_intern("new");
1630
1655
  i_try_convert = rb_intern("try_convert");
1631
1656
  i_uminus = rb_intern("-@");
@@ -73,14 +73,14 @@ static inline SIMD_Implementation find_simd_implementation(void)
73
73
  #define HAVE_SIMD_NEON 1
74
74
 
75
75
  // See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
76
- static ALWAYS_INLINE() uint64_t neon_match_mask(uint8x16_t matches)
76
+ ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches)
77
77
  {
78
78
  const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4);
79
79
  const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
80
80
  return mask & 0x8888888888888888ull;
81
81
  }
82
82
 
83
- static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr)
83
+ ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr)
84
84
  {
85
85
  uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
86
86
 
@@ -93,7 +93,7 @@ static ALWAYS_INLINE() uint64_t compute_chunk_mask_neon(const char *ptr)
93
93
  return neon_match_mask(needs_escape);
94
94
  }
95
95
 
96
- static ALWAYS_INLINE() int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
96
+ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
97
97
  {
98
98
  while (*ptr + sizeof(uint8x16_t) <= end) {
99
99
  uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
@@ -140,7 +140,7 @@ static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
140
140
  #define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))
141
141
  #define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)
142
142
 
143
- static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr)
143
+ ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr)
144
144
  {
145
145
  __m128i chunk = _mm_loadu_si128((__m128i const*)ptr);
146
146
  // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
@@ -151,7 +151,7 @@ static TARGET_SSE2 ALWAYS_INLINE() int compute_chunk_mask_sse2(const char *ptr)
151
151
  return _mm_movemask_epi8(needs_escape);
152
152
  }
153
153
 
154
- static TARGET_SSE2 ALWAYS_INLINE() int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
154
+ ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
155
155
  {
156
156
  while (*ptr + sizeof(__m128i) <= end) {
157
157
  int chunk_mask = compute_chunk_mask_sse2(*ptr);
@@ -29,7 +29,7 @@
29
29
  #include <string.h>
30
30
  #include <stdint.h>
31
31
 
32
- #ifdef JSON_DEBUG
32
+ #if JSON_DEBUG
33
33
  #include <assert.h>
34
34
  #endif
35
35
 
@@ -472,7 +472,7 @@ static int fpconv_dtoa(double d, char dest[28])
472
472
  int ndigits = grisu2(d, digits, &K);
473
473
 
474
474
  str_len += emit_digits(digits, ndigits, dest + str_len, K, neg);
475
- #ifdef JSON_DEBUG
475
+ #if JSON_DEBUG
476
476
  assert(str_len <= 32);
477
477
  #endif
478
478
 
data/lib/json/common.rb CHANGED
@@ -550,6 +550,7 @@ module JSON
550
550
  :create_additions => nil,
551
551
  }
552
552
  # :call-seq:
553
+ # JSON.unsafe_load(source, options = {}) -> object
553
554
  # JSON.unsafe_load(source, proc = nil, options = {}) -> object
554
555
  #
555
556
  # Returns the Ruby objects created by parsing the given +source+.
@@ -681,7 +682,12 @@ module JSON
681
682
  #
682
683
  def unsafe_load(source, proc = nil, options = nil)
683
684
  opts = if options.nil?
684
- _unsafe_load_default_options
685
+ if proc && proc.is_a?(Hash)
686
+ options, proc = proc, nil
687
+ options
688
+ else
689
+ _unsafe_load_default_options
690
+ end
685
691
  else
686
692
  _unsafe_load_default_options.merge(options)
687
693
  end
@@ -709,6 +715,7 @@ module JSON
709
715
  end
710
716
 
711
717
  # :call-seq:
718
+ # JSON.load(source, options = {}) -> object
712
719
  # JSON.load(source, proc = nil, options = {}) -> object
713
720
  #
714
721
  # Returns the Ruby objects created by parsing the given +source+.
@@ -845,8 +852,18 @@ module JSON
845
852
  # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
846
853
  #
847
854
  def load(source, proc = nil, options = nil)
855
+ if proc && options.nil? && proc.is_a?(Hash)
856
+ options = proc
857
+ proc = nil
858
+ end
859
+
848
860
  opts = if options.nil?
849
- _load_default_options
861
+ if proc && proc.is_a?(Hash)
862
+ options, proc = proc, nil
863
+ options
864
+ else
865
+ _load_default_options
866
+ end
850
867
  else
851
868
  _load_default_options.merge(options)
852
869
  end
@@ -1048,7 +1065,7 @@ module JSON
1048
1065
  options[:as_json] = as_json if as_json
1049
1066
 
1050
1067
  @state = State.new(options).freeze
1051
- @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
1068
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
1052
1069
  end
1053
1070
 
1054
1071
  # call-seq:
@@ -1057,7 +1074,7 @@ module JSON
1057
1074
  #
1058
1075
  # Serialize the given object into a \JSON document.
1059
1076
  def dump(object, io = nil)
1060
- @state.generate_new(object, io)
1077
+ @state.generate(object, io)
1061
1078
  end
1062
1079
  alias_method :generate, :dump
1063
1080
 
@@ -312,8 +312,8 @@ module JSON
312
312
  def to_h
313
313
  result = {}
314
314
  instance_variables.each do |iv|
315
- iv = iv.to_s[1..-1]
316
- result[iv.to_sym] = self[iv]
315
+ key = iv.to_s[1..-1]
316
+ result[key.to_sym] = instance_variable_get(iv)
317
317
  end
318
318
 
319
319
  if result[:allow_duplicate_key].nil?
@@ -330,6 +330,9 @@ module JSON
330
330
  # created this method raises a
331
331
  # GeneratorError exception.
332
332
  def generate(obj, anIO = nil)
333
+ return dup.generate(obj, anIO) if frozen?
334
+
335
+ depth = @depth
333
336
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
334
337
  !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
335
338
  result = generate_json(obj, ''.dup)
@@ -346,14 +349,8 @@ module JSON
346
349
  else
347
350
  result
348
351
  end
349
- end
350
-
351
- def generate_new(obj, anIO = nil) # :nodoc:
352
- dup.generate(obj, anIO)
353
- end
354
-
355
- private def initialize_copy(_orig)
356
- @depth = 0
352
+ ensure
353
+ @depth = depth unless frozen?
357
354
  end
358
355
 
359
356
  # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
@@ -494,8 +491,11 @@ module JSON
494
491
  # _depth_ is used to find out nesting depth, to indent accordingly.
495
492
  def to_json(state = nil, *)
496
493
  state = State.from_state(state)
494
+ depth = state.depth
497
495
  state.check_max_nesting
498
496
  json_transform(state)
497
+ ensure
498
+ state.depth = depth
499
499
  end
500
500
 
501
501
  private
@@ -559,17 +559,19 @@ module JSON
559
559
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
560
560
  end
561
561
  result << value.to_json(state)
562
+ state.depth = depth
562
563
  else
563
564
  raise GeneratorError.new("#{value.class} not allowed in JSON", value)
564
565
  end
565
566
  elsif value.respond_to?(:to_json)
566
567
  result << value.to_json(state)
568
+ state.depth = depth
567
569
  else
568
570
  result << %{"#{String(value)}"}
569
571
  end
570
572
  first = false
571
573
  }
572
- depth = state.depth -= 1
574
+ depth -= 1
573
575
  unless first
574
576
  result << state.object_nl
575
577
  result << state.indent * depth if indent
@@ -586,8 +588,11 @@ module JSON
586
588
  # produced JSON string output further.
587
589
  def to_json(state = nil, *)
588
590
  state = State.from_state(state)
591
+ depth = state.depth
589
592
  state.check_max_nesting
590
593
  json_transform(state)
594
+ ensure
595
+ state.depth = depth
591
596
  end
592
597
 
593
598
  private
@@ -625,12 +630,13 @@ module JSON
625
630
  end
626
631
  elsif value.respond_to?(:to_json)
627
632
  result << value.to_json(state)
633
+ state.depth = depth
628
634
  else
629
635
  result << %{"#{String(value)}"}
630
636
  end
631
637
  first = false
632
638
  }
633
- depth = state.depth -= 1
639
+ depth -= 1
634
640
  result << state.array_nl
635
641
  result << state.indent * depth if indent
636
642
  result << ']'
@@ -655,6 +661,9 @@ module JSON
655
661
  if casted_value.equal?(self)
656
662
  raise GeneratorError.new("#{self} not allowed in JSON", self)
657
663
  end
664
+ unless Generator.native_type?(casted_value)
665
+ raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
666
+ end
658
667
 
659
668
  state.check_max_nesting
660
669
  state.depth += 1
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.16.0'
4
+ VERSION = '2.17.0'
5
5
  end
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.16.0
4
+ version: 2.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank