Skip to content

Commit f411ddf

Browse files
committed
Move create_addtions logic in Ruby.
By leveraging the `on_load` callback we can move all this logic out of the parser. Which mean we no longer have to duplicate that logic in both parser and that we'll later be able to extract it entirely from the gem.
1 parent 73d2137 commit f411ddf

File tree

6 files changed

+213
-437
lines changed

6 files changed

+213
-437
lines changed

ext/json/ext/parser/extconf.rb

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
55
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
66
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
7-
have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
87
have_func("strnlen", "string.h") # Missing on Solaris 10
98

109
append_cflags("-std=c99")

ext/json/ext/parser/parser.c

+5-146
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,15 @@ typedef unsigned char _Bool;
3131
static VALUE mJSON, eNestingError, Encoding_UTF_8;
3232
static VALUE CNaN, CInfinity, CMinusInfinity;
3333

34-
static ID i_json_creatable_p, i_json_create, i_create_id,
35-
i_chr, i_deep_const_get, i_match, i_aset, i_aref,
34+
static ID i_chr, i_aset, i_aref,
3635
i_leftshift, i_new, i_try_convert, i_uminus, i_encode;
3736

3837
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
39-
sym_create_additions, sym_create_id, sym_object_class, sym_array_class,
40-
sym_decimal_class, sym_match_string, sym_on_load;
38+
sym_decimal_class, sym_on_load;
4139

4240
static int binary_encindex;
4341
static int utf8_encindex;
4442

45-
#ifdef HAVE_RB_CATEGORY_WARN
46-
# define json_deprecated(message) rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, message)
47-
#else
48-
# define json_deprecated(message) rb_warn(message)
49-
#endif
50-
51-
static const char deprecated_create_additions_warning[] =
52-
"JSON.load implicit support for `create_additions: true` is deprecated "
53-
"and will be removed in 3.0, use JSON.unsafe_load or explicitly "
54-
"pass `create_additions: true`";
55-
5643
#ifndef HAVE_RB_HASH_BULK_INSERT
5744
// For TruffleRuby
5845
void
@@ -445,20 +432,14 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
445432

446433
typedef struct JSON_ParserStruct {
447434
VALUE on_load_proc;
448-
VALUE create_id;
449-
VALUE object_class;
450-
VALUE array_class;
451435
VALUE decimal_class;
452436
ID decimal_method_id;
453-
VALUE match_string;
454437
int max_nesting;
455438
bool allow_nan;
456439
bool allow_trailing_comma;
457440
bool parsing_name;
458441
bool symbolize_names;
459442
bool freeze;
460-
bool create_additions;
461-
bool deprecated_create_additions;
462443
} JSON_ParserConfig;
463444

464445
typedef struct JSON_ParserStateStruct {
@@ -770,18 +751,7 @@ static VALUE json_decode_float(JSON_ParserConfig *config, const char *start, con
770751

771752
static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count)
772753
{
773-
VALUE array;
774-
if (RB_UNLIKELY(config->array_class)) {
775-
array = rb_class_new_instance(0, 0, config->array_class);
776-
VALUE *items = rvalue_stack_peek(state->stack, count);
777-
long index;
778-
for (index = 0; index < count; index++) {
779-
rb_funcall(array, i_leftshift, 1, items[index]);
780-
}
781-
} else {
782-
array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count));
783-
}
784-
754+
VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count));
785755
rvalue_stack_pop(state->stack, count);
786756

787757
if (config->freeze) {
@@ -791,70 +761,20 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
791761
return array;
792762
}
793763

794-
static bool json_obj_creatable_p(VALUE klass)
795-
{
796-
if (rb_respond_to(klass, i_json_creatable_p)) {
797-
return RTEST(rb_funcall(klass, i_json_creatable_p, 0));
798-
} else {
799-
return rb_respond_to(klass, i_json_create);
800-
}
801-
}
802-
803764
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, long count)
804765
{
805-
VALUE object;
806-
if (RB_UNLIKELY(config->object_class)) {
807-
object = rb_class_new_instance(0, 0, config->object_class);
808-
long index = 0;
809-
VALUE *items = rvalue_stack_peek(state->stack, count);
810-
while (index < count) {
811-
VALUE name = items[index++];
812-
VALUE value = items[index++];
813-
rb_funcall(object, i_aset, 2, name, value);
814-
}
815-
} else {
816-
object = rb_hash_new_capa(count);
817-
rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
818-
}
766+
VALUE object = rb_hash_new_capa(count);
767+
rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
819768

820769
rvalue_stack_pop(state->stack, count);
821770

822-
if (RB_UNLIKELY(config->create_additions)) {
823-
VALUE klassname;
824-
if (config->object_class) {
825-
klassname = rb_funcall(object, i_aref, 1, config->create_id);
826-
} else {
827-
klassname = rb_hash_aref(object, config->create_id);
828-
}
829-
if (!NIL_P(klassname)) {
830-
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
831-
if (json_obj_creatable_p(klass)) {
832-
if (config->deprecated_create_additions) {
833-
json_deprecated(deprecated_create_additions_warning);
834-
}
835-
object = rb_funcall(klass, i_json_create, 1, object);
836-
}
837-
}
838-
}
839-
840771
if (config->freeze) {
841772
RB_OBJ_FREEZE(object);
842773
}
843774

844775
return object;
845776
}
846777

847-
static int match_i(VALUE regexp, VALUE klass, VALUE memo)
848-
{
849-
if (regexp == Qundef) return ST_STOP;
850-
if (json_obj_creatable_p(klass) &&
851-
RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
852-
rb_ary_push(memo, klass);
853-
return ST_STOP;
854-
}
855-
return ST_CONTINUE;
856-
}
857-
858778
static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfig *config, const char *start, const char *end, bool escaped, bool is_name)
859779
{
860780
VALUE string;
@@ -866,17 +786,6 @@ static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfi
866786
string = json_string_fastpath(state, start, end, is_name, intern, symbolize);
867787
}
868788

869-
if (RB_UNLIKELY(config->create_additions && RTEST(config->match_string))) {
870-
VALUE klass;
871-
VALUE memo = rb_ary_new2(2);
872-
rb_ary_push(memo, string);
873-
rb_hash_foreach(config->match_string, match_i, memo);
874-
klass = rb_ary_entry(memo, 1);
875-
if (RTEST(klass)) {
876-
string = rb_funcall(klass, i_json_create, 1, string);
877-
}
878-
}
879-
880789
return string;
881790
}
882791

@@ -1229,10 +1138,6 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
12291138
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
12301139
else if (key == sym_freeze) { config->freeze = RTEST(val); }
12311140
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
1232-
else if (key == sym_create_id) { config->create_id = RTEST(val) ? val : Qfalse; }
1233-
else if (key == sym_object_class) { config->object_class = RTEST(val) ? val : Qfalse; }
1234-
else if (key == sym_array_class) { config->array_class = RTEST(val) ? val : Qfalse; }
1235-
else if (key == sym_match_string) { config->match_string = RTEST(val) ? val : Qfalse; }
12361141
else if (key == sym_decimal_class) {
12371142
if (RTEST(val)) {
12381143
if (rb_respond_to(val, i_try_convert)) {
@@ -1262,15 +1167,6 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
12621167
}
12631168
}
12641169
}
1265-
else if (key == sym_create_additions) {
1266-
if (NIL_P(val)) {
1267-
config->create_additions = true;
1268-
config->deprecated_create_additions = true;
1269-
} else {
1270-
config->create_additions = RTEST(val);
1271-
config->deprecated_create_additions = false;
1272-
}
1273-
}
12741170

12751171
return ST_CONTINUE;
12761172
}
@@ -1285,16 +1181,6 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
12851181
// We assume in most cases few keys are set so it's faster to go over
12861182
// the provided keys than to check all possible keys.
12871183
rb_hash_foreach(opts, parser_config_init_i, (VALUE)config);
1288-
1289-
if (config->symbolize_names && config->create_additions) {
1290-
rb_raise(rb_eArgError,
1291-
"options :symbolize_names and :create_additions cannot be "
1292-
" used in conjunction");
1293-
}
1294-
1295-
if (config->create_additions && !config->create_id) {
1296-
config->create_id = rb_funcall(mJSON, i_create_id, 0);
1297-
}
12981184
}
12991185

13001186
}
@@ -1319,15 +1205,6 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
13191205
* (keys) in a JSON object. Otherwise strings are returned, which is
13201206
* also the default. It's not possible to use this option in
13211207
* conjunction with the *create_additions* option.
1322-
* * *create_additions*: If set to false, the Parser doesn't create
1323-
* additions even if a matching class and create_id was found. This option
1324-
* defaults to false.
1325-
* * *object_class*: Defaults to Hash. If another type is provided, it will be used
1326-
* instead of Hash to represent JSON objects. The type must respond to
1327-
* +new+ without arguments, and return an object that respond to +[]=+.
1328-
* * *array_class*: Defaults to Array If another type is provided, it will be used
1329-
* instead of Hash to represent JSON arrays. The type must respond to
1330-
* +new+ without arguments, and return an object that respond to +<<+.
13311208
* * *decimal_class*: Specifies which class to use instead of the default
13321209
* (Float) when parsing decimal numbers. This class must accept a single
13331210
* string argument in its constructor.
@@ -1338,11 +1215,7 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
13381215

13391216
parser_config_init(config, opts);
13401217

1341-
RB_OBJ_WRITTEN(self, Qundef, config->create_id);
1342-
RB_OBJ_WRITTEN(self, Qundef, config->object_class);
1343-
RB_OBJ_WRITTEN(self, Qundef, config->array_class);
13441218
RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
1345-
RB_OBJ_WRITTEN(self, Qundef, config->match_string);
13461219

13471220
return self;
13481221
}
@@ -1406,11 +1279,7 @@ static void JSON_ParserConfig_mark(void *ptr)
14061279
{
14071280
JSON_ParserConfig *config = ptr;
14081281
rb_gc_mark(config->on_load_proc);
1409-
rb_gc_mark(config->create_id);
1410-
rb_gc_mark(config->object_class);
1411-
rb_gc_mark(config->array_class);
14121282
rb_gc_mark(config->decimal_class);
1413-
rb_gc_mark(config->match_string);
14141283
}
14151284

14161285
static void JSON_ParserConfig_free(void *ptr)
@@ -1479,19 +1348,9 @@ void Init_parser(void)
14791348
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
14801349
sym_freeze = ID2SYM(rb_intern("freeze"));
14811350
sym_on_load = ID2SYM(rb_intern("on_load"));
1482-
sym_create_additions = ID2SYM(rb_intern("create_additions"));
1483-
sym_create_id = ID2SYM(rb_intern("create_id"));
1484-
sym_object_class = ID2SYM(rb_intern("object_class"));
1485-
sym_array_class = ID2SYM(rb_intern("array_class"));
14861351
sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
1487-
sym_match_string = ID2SYM(rb_intern("match_string"));
14881352

1489-
i_create_id = rb_intern("create_id");
1490-
i_json_creatable_p = rb_intern("json_creatable?");
1491-
i_json_create = rb_intern("json_create");
14921353
i_chr = rb_intern("chr");
1493-
i_match = rb_intern("match");
1494-
i_deep_const_get = rb_intern("deep_const_get");
14951354
i_aset = rb_intern("[]=");
14961355
i_aref = rb_intern("[]");
14971356
i_leftshift = rb_intern("<<");

0 commit comments

Comments
 (0)