Skip to content

Commit 73d2137

Browse files
committed
JSON.load invoke the proc callback directly from the parser.
And substitute the return value like `Marshal.load` doesm which I can only assume was the intent. This also open the door to re-implement all the `create_addition` logic in `json/common.rb`.
1 parent eda239a commit 73d2137

File tree

6 files changed

+141
-116
lines changed

6 files changed

+141
-116
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changes
22

3+
* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
4+
This better match `Marshal.load` behavior.
35
* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
46
* Deprecate `JSON.load_default_options`.
57
* Deprecate `JSON.unsafe_load_default_options`.

ext/json/ext/parser/parser.c

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id,
3737

3838
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
3939
sym_create_additions, sym_create_id, sym_object_class, sym_array_class,
40-
sym_decimal_class, sym_match_string;
40+
sym_decimal_class, sym_match_string, sym_on_load;
4141

4242
static int binary_encindex;
4343
static int utf8_encindex;
@@ -444,6 +444,7 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
444444
}
445445

446446
typedef struct JSON_ParserStruct {
447+
VALUE on_load_proc;
447448
VALUE create_id;
448449
VALUE object_class;
449450
VALUE array_class;
@@ -879,7 +880,14 @@ static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfi
879880
return string;
880881
}
881882

882-
#define PUSH(result) rvalue_stack_push(state->stack, result, &state->stack_handle, &state->stack)
883+
static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
884+
{
885+
if (RB_UNLIKELY(config->on_load_proc)) {
886+
value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil);
887+
}
888+
rvalue_stack_push(state->stack, value, &state->stack_handle, &state->stack);
889+
return value;
890+
}
883891

884892
static const bool string_scan[256] = {
885893
// ASCII Control Characters
@@ -906,7 +914,7 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
906914
case '"': {
907915
VALUE string = json_decode_string(state, config, start, state->cursor, escaped, is_name);
908916
state->cursor++;
909-
return PUSH(string);
917+
return json_push_value(state, config, string);
910918
}
911919
case '\\': {
912920
state->cursor++;
@@ -940,15 +948,15 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
940948
case 'n':
941949
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "null", 4) == 0)) {
942950
state->cursor += 4;
943-
return PUSH(Qnil);
951+
return json_push_value(state, config, Qnil);
944952
}
945953

946954
raise_parse_error("unexpected token at '%s'", state->cursor);
947955
break;
948956
case 't':
949957
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "true", 4) == 0)) {
950958
state->cursor += 4;
951-
return PUSH(Qtrue);
959+
return json_push_value(state, config, Qtrue);
952960
}
953961

954962
raise_parse_error("unexpected token at '%s'", state->cursor);
@@ -957,7 +965,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
957965
// Note: memcmp with a small power of two compile to an integer comparison
958966
if ((state->end - state->cursor >= 5) && (memcmp(state->cursor + 1, "alse", 4) == 0)) {
959967
state->cursor += 5;
960-
return PUSH(Qfalse);
968+
return json_push_value(state, config, Qfalse);
961969
}
962970

963971
raise_parse_error("unexpected token at '%s'", state->cursor);
@@ -966,15 +974,15 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
966974
// Note: memcmp with a small power of two compile to an integer comparison
967975
if (config->allow_nan && (state->end - state->cursor >= 3) && (memcmp(state->cursor + 1, "aN", 2) == 0)) {
968976
state->cursor += 3;
969-
return PUSH(CNaN);
977+
return json_push_value(state, config, CNaN);
970978
}
971979

972980
raise_parse_error("unexpected token at '%s'", state->cursor);
973981
break;
974982
case 'I':
975983
if (config->allow_nan && (state->end - state->cursor >= 8) && (memcmp(state->cursor, "Infinity", 8) == 0)) {
976984
state->cursor += 8;
977-
return PUSH(CInfinity);
985+
return json_push_value(state, config, CInfinity);
978986
}
979987

980988
raise_parse_error("unexpected token at '%s'", state->cursor);
@@ -984,7 +992,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
984992
if ((state->end - state->cursor >= 9) && (memcmp(state->cursor + 1, "Infinity", 8) == 0)) {
985993
if (config->allow_nan) {
986994
state->cursor += 9;
987-
return PUSH(CMinusInfinity);
995+
return json_push_value(state, config, CMinusInfinity);
988996
} else {
989997
raise_parse_error("unexpected token at '%s'", state->cursor);
990998
}
@@ -1041,9 +1049,9 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
10411049
}
10421050

10431051
if (integer) {
1044-
return PUSH(json_decode_integer(start, state->cursor));
1052+
return json_push_value(state, config, json_decode_integer(start, state->cursor));
10451053
}
1046-
return PUSH(json_decode_float(config, start, state->cursor));
1054+
return json_push_value(state, config, json_decode_float(config, start, state->cursor));
10471055
}
10481056
case '"': {
10491057
// %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"}
@@ -1057,7 +1065,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
10571065

10581066
if ((state->cursor < state->end) && (*state->cursor == ']')) {
10591067
state->cursor++;
1060-
return PUSH(json_decode_array(state, config, 0));
1068+
return json_push_value(state, config, json_decode_array(state, config, 0));
10611069
} else {
10621070
state->current_nesting++;
10631071
if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
@@ -1076,7 +1084,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
10761084
long count = state->stack->head - stack_head;
10771085
state->current_nesting--;
10781086
state->in_array--;
1079-
return PUSH(json_decode_array(state, config, count));
1087+
return json_push_value(state, config, json_decode_array(state, config, count));
10801088
}
10811089

10821090
if (*state->cursor == ',') {
@@ -1103,7 +1111,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
11031111

11041112
if ((state->cursor < state->end) && (*state->cursor == '}')) {
11051113
state->cursor++;
1106-
return PUSH(json_decode_object(state, config, 0));
1114+
return json_push_value(state, config, json_decode_object(state, config, 0));
11071115
} else {
11081116
state->current_nesting++;
11091117
if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
@@ -1132,7 +1140,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
11321140
state->cursor++;
11331141
state->current_nesting--;
11341142
long count = state->stack->head - stack_head;
1135-
return PUSH(json_decode_object(state, config, count));
1143+
return json_push_value(state, config, json_decode_object(state, config, count));
11361144
}
11371145

11381146
if (*state->cursor == ',') {
@@ -1220,6 +1228,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
12201228
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
12211229
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
12221230
else if (key == sym_freeze) { config->freeze = RTEST(val); }
1231+
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
12231232
else if (key == sym_create_id) { config->create_id = RTEST(val) ? val : Qfalse; }
12241233
else if (key == sym_object_class) { config->object_class = RTEST(val) ? val : Qfalse; }
12251234
else if (key == sym_array_class) { config->array_class = RTEST(val) ? val : Qfalse; }
@@ -1396,6 +1405,7 @@ static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
13961405
static void JSON_ParserConfig_mark(void *ptr)
13971406
{
13981407
JSON_ParserConfig *config = ptr;
1408+
rb_gc_mark(config->on_load_proc);
13991409
rb_gc_mark(config->create_id);
14001410
rb_gc_mark(config->object_class);
14011411
rb_gc_mark(config->array_class);
@@ -1468,6 +1478,7 @@ void Init_parser(void)
14681478
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
14691479
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
14701480
sym_freeze = ID2SYM(rb_intern("freeze"));
1481+
sym_on_load = ID2SYM(rb_intern("on_load"));
14711482
sym_create_additions = ID2SYM(rb_intern("create_additions"));
14721483
sym_create_id = ID2SYM(rb_intern("create_id"));
14731484
sym_object_class = ID2SYM(rb_intern("object_class"));

0 commit comments

Comments
 (0)