oj 3.17.7 → 3.17.8

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.
data/ext/oj/resolve.h CHANGED
@@ -4,9 +4,18 @@
4
4
  #ifndef OJ_RESOLVE_H
5
5
  #define OJ_RESOLVE_H
6
6
 
7
+ #include <stdbool.h>
8
+ #include <string.h>
9
+
7
10
  #include "ruby.h"
8
11
 
9
12
  extern VALUE oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class);
10
13
  extern VALUE oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class);
11
14
 
15
+ inline static bool oj_is_backtrace_name(const char *name, size_t len) {
16
+ static const char backtrace[] = "Thread::Backtrace";
17
+
18
+ return sizeof(backtrace) - 1 == len && 0 == memcmp(backtrace, name, len);
19
+ }
20
+
12
21
  #endif /* OJ_RESOLVE_H */
data/ext/oj/saj.c CHANGED
@@ -646,13 +646,14 @@ static VALUE protect_saj_parse(VALUE x) {
646
646
  */
647
647
  VALUE
648
648
  oj_saj_parse(int argc, VALUE *argv, VALUE self) {
649
- char *json = 0;
650
- size_t len = 0;
651
- VALUE input = argv[1];
649
+ char *json = 0;
650
+ size_t len = 0;
651
+ VALUE input;
652
652
 
653
653
  if (argc < 2) {
654
654
  rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
655
655
  }
656
+ input = argv[1];
656
657
  if (rb_type(input) == T_STRING) {
657
658
  // the json string gets modified so make a copy of it
658
659
  len = RSTRING_LEN(input) + 1;
@@ -663,16 +664,20 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
663
664
  volatile VALUE s;
664
665
 
665
666
  if (oj_stringio_class == clas) {
666
- s = rb_funcall2(input, oj_string_id, 0, 0);
667
+ s = rb_funcall2(input, oj_string_id, 0, 0);
668
+ StringValueCStr(s);
667
669
  len = RSTRING_LEN(s) + 1;
668
670
  json = OJ_R_ALLOC_N(char, len);
669
- strcpy(json, StringValueCStr(s));
671
+ strcpy(json, RSTRING_PTR(s));
670
672
  #if !IS_WINDOWS
671
673
  } else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
672
674
  int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
673
675
  ssize_t cnt;
674
676
 
675
677
  len = lseek(fd, 0, SEEK_END);
678
+ if ((size_t)-1 == len) {
679
+ rb_raise(rb_eIOError, "failed to read from IO Object.");
680
+ }
676
681
  lseek(fd, 0, SEEK_SET);
677
682
  json = OJ_R_ALLOC_N(char, len + 1);
678
683
  {
@@ -681,6 +686,7 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
681
686
  while (total < len) {
682
687
  cnt = read(fd, json + total, len - total);
683
688
  if (cnt <= 0) {
689
+ OJ_R_FREE(json);
684
690
  rb_raise(rb_eIOError, "failed to read from IO Object.");
685
691
  }
686
692
  total += cnt;
@@ -689,10 +695,11 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
689
695
  json[len] = '\0';
690
696
  #endif
691
697
  } else if (rb_respond_to(input, oj_read_id)) {
692
- s = rb_funcall2(input, oj_read_id, 0, 0);
698
+ s = rb_funcall2(input, oj_read_id, 0, 0);
699
+ StringValueCStr(s);
693
700
  len = RSTRING_LEN(s) + 1;
694
701
  json = OJ_R_ALLOC_N(char, len);
695
- strcpy(json, StringValueCStr(s));
702
+ strcpy(json, RSTRING_PTR(s));
696
703
  } else {
697
704
  rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
698
705
  }
data/ext/oj/saj2.c CHANGED
@@ -33,9 +33,30 @@ static void push_key(Saj d, VALUE key) {
33
33
  d->tail++;
34
34
  }
35
35
 
36
+ static VALUE pop_key(Saj d) {
37
+ if (d->tail <= d->keys) {
38
+ rb_raise(rb_eIndexError, "accessing key stack");
39
+ }
40
+ d->tail--;
41
+
42
+ return *d->tail;
43
+ }
44
+
36
45
  static void noop(ojParser p) {
37
46
  }
38
47
 
48
+ static void open_push_key(ojParser p) {
49
+ push_key((Saj)p->ctx, get_key(p));
50
+ }
51
+
52
+ static void close_pop_key(ojParser p) {
53
+ Saj d = (Saj)p->ctx;
54
+
55
+ if (d->keys < d->tail) {
56
+ d->tail--;
57
+ }
58
+ }
59
+
39
60
  static void open_object(ojParser p) {
40
61
  rb_funcall(((Saj)p->ctx)->handler, oj_hash_start_id, 1, Qnil);
41
62
  }
@@ -89,11 +110,7 @@ static void close_object(ojParser p) {
89
110
  VALUE key = Qnil;
90
111
 
91
112
  if (OBJECT_FUN == p->stack[p->depth]) {
92
- d->tail--;
93
- if (d->tail < d->keys) {
94
- rb_raise(rb_eIndexError, "accessing key stack");
95
- }
96
- key = *d->tail;
113
+ key = pop_key(d);
97
114
  }
98
115
  rb_funcall(d->handler, oj_hash_end_id, 1, key);
99
116
  }
@@ -103,11 +120,7 @@ static void close_object_loc(ojParser p) {
103
120
  VALUE key = Qnil;
104
121
 
105
122
  if (OBJECT_FUN == p->stack[p->depth]) {
106
- d->tail--;
107
- if (d->tail < d->keys) {
108
- rb_raise(rb_eIndexError, "accessing key stack");
109
- }
110
- key = *d->tail;
123
+ key = pop_key(d);
111
124
  }
112
125
  rb_funcall(d->handler, oj_hash_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
113
126
  }
@@ -117,11 +130,7 @@ static void close_array(ojParser p) {
117
130
  VALUE key = Qnil;
118
131
 
119
132
  if (OBJECT_FUN == p->stack[p->depth]) {
120
- d->tail--;
121
- if (d->tail < d->keys) {
122
- rb_raise(rb_eIndexError, "accessing key stack");
123
- }
124
- key = *d->tail;
133
+ key = pop_key(d);
125
134
  }
126
135
  rb_funcall(d->handler, oj_array_end_id, 1, key);
127
136
  }
@@ -131,11 +140,7 @@ static void close_array_loc(ojParser p) {
131
140
  VALUE key = Qnil;
132
141
 
133
142
  if (OBJECT_FUN == p->stack[p->depth]) {
134
- d->tail--;
135
- if (d->tail < d->keys) {
136
- rb_raise(rb_eIndexError, "accessing key stack");
137
- }
138
- key = *d->tail;
143
+ key = pop_key(d);
139
144
  }
140
145
  rb_funcall(d->handler, oj_array_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
141
146
  }
@@ -380,10 +385,15 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
380
385
  return d->handler;
381
386
  }
382
387
  if (0 == strcmp(key, "handler=")) {
388
+ int hash_start = rb_respond_to(value, oj_hash_start_id);
389
+ int hash_end = rb_respond_to(value, oj_hash_end_id);
390
+ int array_start = rb_respond_to(value, oj_array_start_id);
391
+ int array_end = rb_respond_to(value, oj_array_end_id);
392
+
383
393
  d->tail = d->keys;
384
394
  d->handler = value;
385
395
  reset(p);
386
- if (rb_respond_to(value, oj_hash_start_id)) {
396
+ if (hash_start) {
387
397
  if (1 == rb_obj_method_arity(value, oj_hash_start_id)) {
388
398
  p->funcs[TOP_FUN].open_object = open_object;
389
399
  p->funcs[ARRAY_FUN].open_object = open_object;
@@ -393,8 +403,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
393
403
  p->funcs[ARRAY_FUN].open_object = open_object_loc;
394
404
  p->funcs[OBJECT_FUN].open_object = open_object_loc_key;
395
405
  }
406
+ } else if (hash_end) {
407
+ p->funcs[OBJECT_FUN].open_object = open_push_key;
396
408
  }
397
- if (rb_respond_to(value, oj_array_start_id)) {
409
+ if (array_start) {
398
410
  if (1 == rb_obj_method_arity(value, oj_array_start_id)) {
399
411
  p->funcs[TOP_FUN].open_array = open_array;
400
412
  p->funcs[ARRAY_FUN].open_array = open_array;
@@ -404,8 +416,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
404
416
  p->funcs[ARRAY_FUN].open_array = open_array_loc;
405
417
  p->funcs[OBJECT_FUN].open_array = open_array_loc_key;
406
418
  }
419
+ } else if (array_end) {
420
+ p->funcs[OBJECT_FUN].open_array = open_push_key;
407
421
  }
408
- if (rb_respond_to(value, oj_hash_end_id)) {
422
+ if (hash_end) {
409
423
  if (1 == rb_obj_method_arity(value, oj_hash_end_id)) {
410
424
  p->funcs[TOP_FUN].close_object = close_object;
411
425
  p->funcs[ARRAY_FUN].close_object = close_object;
@@ -415,8 +429,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
415
429
  p->funcs[ARRAY_FUN].close_object = close_object_loc;
416
430
  p->funcs[OBJECT_FUN].close_object = close_object_loc;
417
431
  }
432
+ } else if (hash_start) {
433
+ p->funcs[OBJECT_FUN].close_object = close_pop_key;
418
434
  }
419
- if (rb_respond_to(value, oj_array_end_id)) {
435
+ if (array_end) {
420
436
  if (1 == rb_obj_method_arity(value, oj_array_end_id)) {
421
437
  p->funcs[TOP_FUN].close_array = close_array;
422
438
  p->funcs[ARRAY_FUN].close_array = close_array;
@@ -426,6 +442,8 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
426
442
  p->funcs[ARRAY_FUN].close_array = close_array_loc;
427
443
  p->funcs[OBJECT_FUN].close_array = close_array_loc;
428
444
  }
445
+ } else if (array_start) {
446
+ p->funcs[OBJECT_FUN].close_array = close_pop_key;
429
447
  }
430
448
  if (rb_respond_to(value, oj_add_value_id)) {
431
449
  if (2 == rb_obj_method_arity(value, oj_add_value_id)) {
@@ -547,10 +565,8 @@ static void mark(ojParser p) {
547
565
  if (Qnil != d->handler) {
548
566
  rb_gc_mark(d->handler);
549
567
  }
550
- if (!d->cache_keys) {
551
- for (kp = d->keys; kp < d->tail; kp++) {
552
- rb_gc_mark(*kp);
553
- }
568
+ for (kp = d->keys; kp < d->tail; kp++) {
569
+ rb_gc_mark(*kp);
554
570
  }
555
571
  }
556
572
 
data/ext/oj/simd.h CHANGED
@@ -98,12 +98,10 @@ static inline int oj_ctz64_fallback(uint64_t x) {
98
98
  #elif defined(__GNUC__) || defined(__clang__)
99
99
  // GCC/Clang: check for header availability and include them
100
100
  // We include headers but use target attributes to enable instructions per-function
101
- // Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable
102
- #ifdef __has_include
103
- #if __has_include(<cpuid.h>)
101
+ // Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable.
102
+ // cpuid.h is available on all GCC since 4.3 and all Clang versions, so include
103
+ // it unconditionally rather than gating on __has_include (which GCC < 5 lacks).
104
104
  #include <cpuid.h>
105
- #endif
106
- #endif
107
105
  #if defined(__SSE4_2__) || defined(__SSE2__)
108
106
  // If any SSE is enabled globally, x86intrin.h should be available
109
107
  #include <x86intrin.h>
data/ext/oj/sparse.c CHANGED
@@ -274,6 +274,10 @@ static void read_escaped_str(ParseInfo pi) {
274
274
  buf_cleanup(&buf);
275
275
  return;
276
276
  }
277
+ } else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
278
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
279
+ buf_cleanup(&buf);
280
+ return;
277
281
  } else {
278
282
  buf_append(&buf, c);
279
283
  }
@@ -341,6 +345,9 @@ static void read_str(ParseInfo pi) {
341
345
  read_escaped_str(pi);
342
346
  reader_release(&pi->rd);
343
347
  return;
348
+ } else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
349
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
350
+ return;
344
351
  }
345
352
  }
346
353
  if (0 == parent) { // simple add
@@ -16,6 +16,9 @@ static void stream_writer_free(void *ptr) {
16
16
  return;
17
17
  }
18
18
  sw = (StreamWriter)ptr;
19
+ if (NULL != sw->sw.out.circ_cache) {
20
+ oj_cache8_delete(sw->sw.out.circ_cache);
21
+ }
19
22
  oj_options_release(&sw->sw.opts);
20
23
  OJ_R_FREE(sw->sw.out.buf);
21
24
  OJ_R_FREE(sw->sw.types);
@@ -169,6 +169,11 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
169
169
  *out->cur++ = ':';
170
170
  }
171
171
  }
172
+ if (NULL != out->circ_cache) {
173
+ oj_cache8_delete(out->circ_cache);
174
+ out->circ_cache = NULL;
175
+ }
176
+ out->circ_cnt = 0;
172
177
  switch (out->opts->mode) {
173
178
  case StrictMode: oj_dump_strict_val(val, sw->depth, out); break;
174
179
  case NullMode: oj_dump_null_val(val, sw->depth, out); break;
@@ -178,6 +183,10 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
178
183
  case CustomMode: oj_dump_custom_val(val, sw->depth, out, true); break;
179
184
  default: oj_dump_custom_val(val, sw->depth, out, true); break;
180
185
  }
186
+ if (NULL != out->circ_cache) {
187
+ oj_cache8_delete(out->circ_cache);
188
+ out->circ_cache = NULL;
189
+ }
181
190
  }
182
191
 
183
192
  void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
@@ -203,16 +212,17 @@ void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
203
212
 
204
213
  void oj_str_writer_pop(StrWriter sw) {
205
214
  long size;
206
- DumpType type = sw->types[sw->depth];
215
+ DumpType type;
207
216
 
208
217
  if (sw->keyWritten) {
209
218
  sw->keyWritten = 0;
210
219
  rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
211
220
  }
212
- sw->depth--;
213
- if (0 > sw->depth) {
221
+ if (0 >= sw->depth) {
214
222
  rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
215
223
  }
224
+ type = sw->types[sw->depth];
225
+ sw->depth--;
216
226
  size = sw->depth * sw->out.indent + 2;
217
227
  assure_size(&sw->out, size);
218
228
  fill_indent(&sw->out, sw->depth);
@@ -241,6 +251,10 @@ static void string_writer_free(void *ptr) {
241
251
  }
242
252
  sw = (StrWriter)ptr;
243
253
 
254
+ if (NULL != sw->out.circ_cache) {
255
+ oj_cache8_delete(sw->out.circ_cache);
256
+ sw->out.circ_cache = NULL;
257
+ }
244
258
  oj_out_free(&sw->out);
245
259
  oj_options_release(&sw->opts);
246
260
 
data/ext/oj/usual.c CHANGED
@@ -85,6 +85,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, bool auto_defin
85
85
 
86
86
  if (rb_const_defined_at(mod, ci)) {
87
87
  clas = rb_const_get_at(mod, ci);
88
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
89
+ clas = Qundef;
90
+ }
88
91
  } else if (auto_define) {
89
92
  clas = rb_define_class_under(mod, classname, oj_bag_class);
90
93
  } else {
@@ -814,7 +817,7 @@ static VALUE opt_class_cache_set(ojParser p, VALUE value) {
814
817
 
815
818
  if (Qtrue == value) {
816
819
  if (NULL == d->class_cache) {
817
- d->class_cache = cache_create(0, form_class_auto, MISS_AUTO == d->miss_class, false);
820
+ d->class_cache = cache_create(0, form_class_auto, true, false);
818
821
  }
819
822
  } else if (NULL != d->class_cache) {
820
823
  cache_free(d->class_cache);
data/ext/oj/val_stack.c CHANGED
@@ -3,8 +3,6 @@
3
3
 
4
4
  #include "val_stack.h"
5
5
 
6
- #include <string.h>
7
-
8
6
  #include "odd.h"
9
7
  #include "oj.h"
10
8
 
@@ -15,12 +13,6 @@ static void stack_mark(void *ptr) {
15
13
  if (NULL == ptr) {
16
14
  return;
17
15
  }
18
- #ifdef HAVE_PTHREAD_MUTEX_INIT
19
- pthread_mutex_lock(&stack->mutex);
20
- #else
21
- rb_mutex_lock(stack->mutex);
22
- rb_gc_mark(stack->mutex);
23
- #endif
24
16
  {
25
17
  size_t i;
26
18
 
@@ -46,11 +38,6 @@ static void stack_mark(void *ptr) {
46
38
  }
47
39
  }
48
40
  }
49
- #ifdef HAVE_PTHREAD_MUTEX_INIT
50
- pthread_mutex_unlock(&stack->mutex);
51
- #else
52
- rb_mutex_unlock(stack->mutex);
53
- #endif
54
41
  }
55
42
 
56
43
  static const rb_data_type_t oj_stack_type = {
@@ -66,15 +53,6 @@ static const rb_data_type_t oj_stack_type = {
66
53
 
67
54
  VALUE
68
55
  oj_stack_init(ValStack stack) {
69
- #ifdef HAVE_PTHREAD_MUTEX_INIT
70
- int err;
71
-
72
- if (0 != (err = pthread_mutex_init(&stack->mutex, 0))) {
73
- rb_raise(rb_eException, "failed to initialize a mutex. %s", strerror(err));
74
- }
75
- #else
76
- stack->mutex = rb_mutex_new();
77
- #endif
78
56
  stack->head = stack->base;
79
57
  stack->end = stack->base + sizeof(stack->base) / sizeof(struct _val);
80
58
  stack->tail = stack->head;
data/ext/oj/val_stack.h CHANGED
@@ -9,9 +9,6 @@
9
9
  #include "mem.h"
10
10
  #include "odd.h"
11
11
  #include "ruby.h"
12
- #ifdef HAVE_PTHREAD_MUTEX_INIT
13
- #include <pthread.h>
14
- #endif
15
12
 
16
13
  #define STACK_INC 64
17
14
 
@@ -58,12 +55,6 @@ typedef struct _valStack {
58
55
  Val head; // current stack
59
56
  Val end; // stack end
60
57
  Val tail; // pointer to one past last element name on stack
61
- #ifdef HAVE_PTHREAD_MUTEX_INIT
62
- pthread_mutex_t mutex;
63
- #else
64
- VALUE mutex;
65
- #endif
66
-
67
58
  } *ValStack;
68
59
 
69
60
  extern VALUE oj_stack_init(ValStack stack);
@@ -102,8 +93,8 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
102
93
  const char *old = (const char *)stack->head;
103
94
  size_t i;
104
95
 
105
- // A realloc can trigger a GC so make sure it happens outside the lock
106
- // but lock before changing pointers.
96
+ // A realloc can trigger a GC that marks the stack, so change the
97
+ // pointers only after it returns.
107
98
  if (stack->base == stack->head) {
108
99
  head = OJ_R_ALLOC_N(struct _val, len + STACK_INC);
109
100
  memcpy(head, stack->base, sizeof(struct _val) * len);
@@ -117,19 +108,9 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
117
108
  head[i].key = head[i].karray;
118
109
  }
119
110
  }
120
- #ifdef HAVE_PTHREAD_MUTEX_INIT
121
- pthread_mutex_lock(&stack->mutex);
122
- #else
123
- rb_mutex_lock(stack->mutex);
124
- #endif
125
111
  stack->head = head;
126
112
  stack->tail = stack->head + toff;
127
113
  stack->end = stack->head + len + STACK_INC;
128
- #ifdef HAVE_PTHREAD_MUTEX_INIT
129
- pthread_mutex_unlock(&stack->mutex);
130
- #else
131
- rb_mutex_unlock(stack->mutex);
132
- #endif
133
114
  }
134
115
  stack->tail->val = val;
135
116
  stack->tail->next = next;
@@ -183,9 +164,8 @@ inline static Val stack_pop(ValStack stack) {
183
164
  }
184
165
 
185
166
  // Append one key/value pair to the pair buffer. The same GC discipline as
186
- // stack_push: the allocation happens outside the mutex (it can trigger a GC
187
- // which takes the mutex in stack_mark) and the buffer pointer only changes
188
- // under the mutex.
167
+ // stack_push: the allocation can trigger a GC that marks the pairs, so the
168
+ // buffer pointer only changes after it returns.
189
169
  inline static void stack_pair_push(ValStack stack, VALUE key, VALUE value) {
190
170
  if (stack->pend <= stack->pcnt + 2) {
191
171
  size_t cnt = stack->pend * 2;
@@ -198,18 +178,8 @@ inline static void stack_pair_push(ValStack stack, VALUE key, VALUE value) {
198
178
  pairs = stack->pairs;
199
179
  OJ_R_REALLOC_N(pairs, VALUE, cnt);
200
180
  }
201
- #ifdef HAVE_PTHREAD_MUTEX_INIT
202
- pthread_mutex_lock(&stack->mutex);
203
- #else
204
- rb_mutex_lock(stack->mutex);
205
- #endif
206
181
  stack->pairs = pairs;
207
182
  stack->pend = cnt;
208
- #ifdef HAVE_PTHREAD_MUTEX_INIT
209
- pthread_mutex_unlock(&stack->mutex);
210
- #else
211
- rb_mutex_unlock(stack->mutex);
212
- #endif
213
183
  }
214
184
  stack->pairs[stack->pcnt] = key;
215
185
  stack->pairs[stack->pcnt + 1] = value;
data/ext/oj/wab.c CHANGED
@@ -40,6 +40,7 @@ static VALUE resolve_wab_uuid_class(void) {
40
40
  volatile VALUE wab_module;
41
41
 
42
42
  wab_uuid_clas = Qnil;
43
+ rb_gc_register_address(&wab_uuid_clas);
43
44
  if (rb_const_defined_at(rb_cObject, rb_intern("WAB"))) {
44
45
  wab_module = rb_const_get_at(rb_cObject, rb_intern("WAB"));
45
46
  if (rb_const_defined_at(wab_module, rb_intern("UUID"))) {
@@ -53,6 +54,7 @@ static VALUE resolve_wab_uuid_class(void) {
53
54
  static VALUE resolve_uri_class(void) {
54
55
  if (Qundef == uri_clas) {
55
56
  uri_clas = Qnil;
57
+ rb_gc_register_address(&uri_clas);
56
58
  if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
57
59
  uri_clas = rb_const_get_at(rb_cObject, rb_intern("URI"));
58
60
  }
@@ -65,6 +67,7 @@ static VALUE resolve_uri_http_class(void) {
65
67
  volatile VALUE uri_module;
66
68
 
67
69
  uri_http_clas = Qnil;
70
+ rb_gc_register_address(&uri_http_clas);
68
71
  if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
69
72
  uri_module = rb_const_get_at(rb_cObject, rb_intern("URI"));
70
73
  if (rb_const_defined_at(uri_module, rb_intern("HTTP"))) {
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Loaded on first use of JSON::GenericObject so that requiring Oj does not
4
+ # also require ostruct, which is no longer a default gem as of Ruby 4.0.
5
+ begin
6
+ require 'ostruct'
7
+ rescue LoadError
8
+ raise LoadError.new("JSON::GenericObject requires 'ostruct'. Add it to your Gemfile or install it with `gem install ostruct`.")
9
+ end
10
+
11
+ module JSON
12
+ # Taken directly from the json gem. Shamelessly copied. It is similar in
13
+ # some ways to the Oj::Bag class or the Oj::EasyHash class.
14
+ class GenericObject < OpenStruct
15
+ class << self
16
+ alias [] new
17
+
18
+ def json_creatable?
19
+ @json_creatable
20
+ end
21
+
22
+ attr_writer :json_creatable
23
+
24
+ def json_create(data)
25
+ data = data.dup
26
+ data.delete JSON.create_id
27
+ self[data]
28
+ end
29
+
30
+ def from_hash(object)
31
+ case
32
+ when object.respond_to?(:to_hash)
33
+ result = new
34
+ object.to_hash.each do |key, value|
35
+ result[key] = from_hash(value)
36
+ end
37
+ result
38
+ when object.respond_to?(:to_ary)
39
+ object.to_ary.map { |a| from_hash(a) }
40
+ else
41
+ object
42
+ end
43
+ end
44
+
45
+ def load(source, proc = nil, opts = {})
46
+ result = ::JSON.load(source, proc, opts.merge(:object_class => self))
47
+ result.nil? ? new : result
48
+ end
49
+
50
+ def dump(obj, *args)
51
+ ::JSON.dump(obj, *args)
52
+ end
53
+
54
+ end # self
55
+
56
+ self.json_creatable = false
57
+
58
+ def to_hash
59
+ table
60
+ end
61
+
62
+ def [](name)
63
+ __send__(name)
64
+ end unless method_defined?(:[])
65
+
66
+ def []=(name, value)
67
+ __send__("#{name}=", value)
68
+ end unless method_defined?(:[]=)
69
+
70
+ def |(other)
71
+ self.class[other.to_hash.merge(to_hash)]
72
+ end
73
+
74
+ def as_json(*)
75
+ { JSON.create_id => self.class.name }.merge to_hash
76
+ end
77
+
78
+ def to_json(*a)
79
+ as_json.to_json(*a)
80
+ end
81
+ end
82
+ end # JSON
data/lib/oj/json.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'ostruct'
2
1
  require 'oj/state'
3
2
 
4
3
  if defined?(JSON::PRETTY_STATE_PROTOTYPE)
@@ -113,76 +112,7 @@ unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
113
112
  self.parser = ::JSON::Ext::Parser
114
113
  self.generator = ::JSON::Ext::Generator
115
114
 
116
- # Taken directly from the json gem. Shamelessly copied. It is similar in
117
- # some ways to the Oj::Bag class or the Oj::EasyHash class.
118
- class GenericObject < OpenStruct
119
- class << self
120
- alias [] new
121
-
122
- def json_creatable?
123
- @json_creatable
124
- end
125
-
126
- attr_writer :json_creatable
127
-
128
- def json_create(data)
129
- data = data.dup
130
- data.delete JSON.create_id
131
- self[data]
132
- end
133
-
134
- def from_hash(object)
135
- case
136
- when object.respond_to?(:to_hash)
137
- result = new
138
- object.to_hash.each do |key, value|
139
- result[key] = from_hash(value)
140
- end
141
- result
142
- when object.respond_to?(:to_ary)
143
- object.to_ary.map { |a| from_hash(a) }
144
- else
145
- object
146
- end
147
- end
148
-
149
- def load(source, proc = nil, opts = {})
150
- result = ::JSON.load(source, proc, opts.merge(:object_class => self))
151
- result.nil? ? new : result
152
- end
153
-
154
- def dump(obj, *args)
155
- ::JSON.dump(obj, *args)
156
- end
157
-
158
- end # self
159
-
160
- self.json_creatable = false
161
-
162
- def to_hash
163
- table
164
- end
165
-
166
- def [](name)
167
- __send__(name)
168
- end unless method_defined?(:[])
169
-
170
- def []=(name, value)
171
- __send__("#{name}=", value)
172
- end unless method_defined?(:[]=)
173
-
174
- def |(other)
175
- self.class[other.to_hash.merge(to_hash)]
176
- end
177
-
178
- def as_json(*)
179
- { JSON.create_id => self.class.name }.merge to_hash
180
- end
181
-
182
- def to_json(*a)
183
- as_json.to_json(*a)
184
- end
185
- end
115
+ autoload :GenericObject, 'oj/generic_object'
186
116
 
187
117
  end # JSON
188
118
  end
data/lib/oj/mimic.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  require 'bigdecimal'
4
- require 'ostruct'
5
4
 
6
5
  module Oj
7
6