diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c
index cf0b3cefa..37f92fcbe 100644
--- a/ext/json/ext/parser/parser.c
+++ b/ext/json/ext/parser/parser.c
@@ -474,6 +474,9 @@ case 26:
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
+ if (json->deprecated_create_additions) {
+ rb_warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -486,7 +489,7 @@ case 26:
-#line 490 "parser.c"
+#line 493 "parser.c"
enum {JSON_value_start = 1};
enum {JSON_value_first_final = 29};
enum {JSON_value_error = 0};
@@ -494,7 +497,7 @@ enum {JSON_value_error = 0};
enum {JSON_value_en_main = 1};
-#line 287 "parser.rl"
+#line 290 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -502,14 +505,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 506 "parser.c"
+#line 509 "parser.c"
{
cs = JSON_value_start;
}
-#line 294 "parser.rl"
+#line 297 "parser.rl"
-#line 513 "parser.c"
+#line 516 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -543,14 +546,14 @@ case 1:
cs = 0;
goto _out;
tr2:
-#line 239 "parser.rl"
+#line 242 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
}
goto st29;
tr3:
-#line 244 "parser.rl"
+#line 247 "parser.rl"
{
char *np;
if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
@@ -570,7 +573,7 @@ cs = 0;
}
goto st29;
tr7:
-#line 262 "parser.rl"
+#line 265 "parser.rl"
{
char *np;
np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
@@ -578,7 +581,7 @@ cs = 0;
}
goto st29;
tr11:
-#line 268 "parser.rl"
+#line 271 "parser.rl"
{
char *np;
np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
@@ -586,7 +589,7 @@ cs = 0;
}
goto st29;
tr25:
-#line 232 "parser.rl"
+#line 235 "parser.rl"
{
if (json->allow_nan) {
*result = CInfinity;
@@ -596,7 +599,7 @@ cs = 0;
}
goto st29;
tr27:
-#line 225 "parser.rl"
+#line 228 "parser.rl"
{
if (json->allow_nan) {
*result = CNaN;
@@ -606,19 +609,19 @@ cs = 0;
}
goto st29;
tr31:
-#line 219 "parser.rl"
+#line 222 "parser.rl"
{
*result = Qfalse;
}
goto st29;
tr34:
-#line 216 "parser.rl"
+#line 219 "parser.rl"
{
*result = Qnil;
}
goto st29;
tr37:
-#line 222 "parser.rl"
+#line 225 "parser.rl"
{
*result = Qtrue;
}
@@ -627,9 +630,9 @@ cs = 0;
if ( ++p == pe )
goto _test_eof29;
case 29:
-#line 274 "parser.rl"
+#line 277 "parser.rl"
{ p--; {p++; cs = 29; goto _out;} }
-#line 633 "parser.c"
+#line 636 "parser.c"
switch( (*p) ) {
case 13: goto st29;
case 32: goto st29;
@@ -870,7 +873,7 @@ case 28:
_out: {}
}
-#line 295 "parser.rl"
+#line 298 "parser.rl"
if (json->freeze) {
OBJ_FREEZE(*result);
@@ -884,7 +887,7 @@ case 28:
}
-#line 888 "parser.c"
+#line 891 "parser.c"
enum {JSON_integer_start = 1};
enum {JSON_integer_first_final = 3};
enum {JSON_integer_error = 0};
@@ -892,7 +895,7 @@ enum {JSON_integer_error = 0};
enum {JSON_integer_en_main = 1};
-#line 315 "parser.rl"
+#line 318 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -900,15 +903,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 904 "parser.c"
+#line 907 "parser.c"
{
cs = JSON_integer_start;
}
-#line 322 "parser.rl"
+#line 325 "parser.rl"
json->memo = p;
-#line 912 "parser.c"
+#line 915 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -942,14 +945,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 312 "parser.rl"
+#line 315 "parser.rl"
{ p--; {p++; cs = 4; goto _out;} }
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
-#line 953 "parser.c"
+#line 956 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -968,7 +971,7 @@ case 5:
_out: {}
}
-#line 324 "parser.rl"
+#line 327 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -983,7 +986,7 @@ case 5:
}
-#line 987 "parser.c"
+#line 990 "parser.c"
enum {JSON_float_start = 1};
enum {JSON_float_first_final = 8};
enum {JSON_float_error = 0};
@@ -991,7 +994,7 @@ enum {JSON_float_error = 0};
enum {JSON_float_en_main = 1};
-#line 349 "parser.rl"
+#line 352 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -999,15 +1002,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 1003 "parser.c"
+#line 1006 "parser.c"
{
cs = JSON_float_start;
}
-#line 356 "parser.rl"
+#line 359 "parser.rl"
json->memo = p;
-#line 1011 "parser.c"
+#line 1014 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1065,14 +1068,14 @@ case 8:
goto st0;
goto tr9;
tr9:
-#line 343 "parser.rl"
+#line 346 "parser.rl"
{ p--; {p++; cs = 9; goto _out;} }
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 1076 "parser.c"
+#line 1079 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -1133,7 +1136,7 @@ case 7:
_out: {}
}
-#line 358 "parser.rl"
+#line 361 "parser.rl"
if (cs >= JSON_float_first_final) {
VALUE mod = Qnil;
@@ -1186,7 +1189,7 @@ case 7:
-#line 1190 "parser.c"
+#line 1193 "parser.c"
enum {JSON_array_start = 1};
enum {JSON_array_first_final = 17};
enum {JSON_array_error = 0};
@@ -1194,7 +1197,7 @@ enum {JSON_array_error = 0};
enum {JSON_array_en_main = 1};
-#line 438 "parser.rl"
+#line 441 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -1208,14 +1211,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
-#line 1212 "parser.c"
+#line 1215 "parser.c"
{
cs = JSON_array_start;
}
-#line 451 "parser.rl"
+#line 454 "parser.rl"
-#line 1219 "parser.c"
+#line 1222 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1254,7 +1257,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 415 "parser.rl"
+#line 418 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -1274,7 +1277,7 @@ case 2:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 1278 "parser.c"
+#line 1281 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -1374,14 +1377,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 430 "parser.rl"
+#line 433 "parser.rl"
{ p--; {p++; cs = 17; goto _out;} }
goto st17;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
-#line 1385 "parser.c"
+#line 1388 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1437,7 +1440,7 @@ case 16:
_out: {}
}
-#line 452 "parser.rl"
+#line 455 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1598,7 +1601,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
-#line 1602 "parser.c"
+#line 1605 "parser.c"
enum {JSON_string_start = 1};
enum {JSON_string_first_final = 8};
enum {JSON_string_error = 0};
@@ -1606,7 +1609,7 @@ enum {JSON_string_error = 0};
enum {JSON_string_en_main = 1};
-#line 630 "parser.rl"
+#line 633 "parser.rl"
static int
@@ -1627,15 +1630,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
VALUE match_string;
-#line 1631 "parser.c"
+#line 1634 "parser.c"
{
cs = JSON_string_start;
}
-#line 650 "parser.rl"
+#line 653 "parser.rl"
json->memo = p;
-#line 1639 "parser.c"
+#line 1642 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1660,7 +1663,7 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 617 "parser.rl"
+#line 620 "parser.rl"
{
*result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
if (NIL_P(*result)) {
@@ -1670,14 +1673,14 @@ case 2:
{p = (( p + 1))-1;}
}
}
-#line 627 "parser.rl"
+#line 630 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1681 "parser.c"
+#line 1684 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1753,7 +1756,7 @@ case 7:
_out: {}
}
-#line 652 "parser.rl"
+#line 655 "parser.rl"
if (json->create_additions && RTEST(match_string = json->match_string)) {
VALUE klass;
@@ -1888,10 +1891,16 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
+ tmp = rb_hash_aref(opts, tmp);
+ if (NIL_P(tmp)) {
+ json->create_additions = true;
+ json->deprecated_create_additions = true;
+ } else {
+ json->create_additions = RTEST(tmp);
+ json->deprecated_create_additions = false;
+ }
}
+
if (json->symbolize_names && json->create_additions) {
rb_raise(rb_eArgError,
"options :symbolize_names and :create_additions cannot be "
@@ -1946,7 +1955,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
-#line 1950 "parser.c"
+#line 1959 "parser.c"
enum {JSON_start = 1};
enum {JSON_first_final = 10};
enum {JSON_error = 0};
@@ -1954,7 +1963,7 @@ enum {JSON_error = 0};
enum {JSON_en_main = 1};
-#line 858 "parser.rl"
+#line 867 "parser.rl"
/*
@@ -1972,16 +1981,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1976 "parser.c"
+#line 1985 "parser.c"
{
cs = JSON_start;
}
-#line 875 "parser.rl"
+#line 884 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1985 "parser.c"
+#line 1994 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -2015,7 +2024,7 @@ case 1:
cs = 0;
goto _out;
tr2:
-#line 850 "parser.rl"
+#line 859 "parser.rl"
{
char *np = JSON_parse_value(json, p, pe, &result, 0);
if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
@@ -2025,7 +2034,7 @@ cs = 0;
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 2029 "parser.c"
+#line 2038 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -2114,7 +2123,7 @@ case 9:
_out: {}
}
-#line 878 "parser.rl"
+#line 887 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h
index 59c502b22..6838d2fc0 100644
--- a/ext/json/ext/parser/parser.h
+++ b/ext/json/ext/parser/parser.h
@@ -26,6 +26,7 @@ typedef struct JSON_ParserStruct {
char symbolize_names;
char freeze;
char create_additions;
+ char deprecated_create_additions;
} JSON_Parser;
#define GET_PARSER \
diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl
index 73f81341a..c11d547bd 100644
--- a/ext/json/ext/parser/parser.rl
+++ b/ext/json/ext/parser/parser.rl
@@ -196,6 +196,9 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
+ if (json->deprecated_create_additions) {
+ rb_warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -783,10 +786,16 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
+ tmp = rb_hash_aref(opts, tmp);
+ if (NIL_P(tmp)) {
+ json->create_additions = true;
+ json->deprecated_create_additions = true;
+ } else {
+ json->create_additions = RTEST(tmp);
+ json->deprecated_create_additions = false;
+ }
}
+
if (json->symbolize_names && json->create_additions) {
rb_raise(rb_eArgError,
"options :symbolize_names and :create_additions cannot be "
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index 4234fd21f..92001b3e0 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -51,6 +51,7 @@ public class Parser extends RubyObject {
private RubyString vSource;
private RubyString createId;
private boolean createAdditions;
+ private boolean deprecatedCreateAdditions;
private int maxNesting;
private boolean allowNaN;
private boolean symbolizeNames;
@@ -171,7 +172,20 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
this.symbolizeNames = opts.getBool("symbolize_names", false);
this.freeze = opts.getBool("freeze", false);
this.createId = opts.getString("create_id", getCreateId(context));
- this.createAdditions = opts.getBool("create_additions", false);
+
+ IRubyObject additions = opts.get("create_additions");
+ this.createAdditions = false;
+ this.deprecatedCreateAdditions = false;
+
+ if (additions != null) {
+ if (additions.isNil()) {
+ this.createAdditions = true;
+ this.deprecatedCreateAdditions = true;
+ } else {
+ this.createAdditions = opts.getBool("create_additions", false);
+ }
+ }
+
this.objectClass = opts.getClass("object_class", runtime.getHash());
this.arrayClass = opts.getClass("array_class", runtime.getArray());
this.decimalClass = opts.getClass("decimal_class", null);
@@ -342,11 +356,11 @@ private Ruby getRuntime() {
}
-// line 368 "Parser.rl"
+// line 382 "Parser.rl"
-// line 350 "Parser.java"
+// line 364 "Parser.java"
private static byte[] init__JSON_value_actions_0()
{
return new byte [] {
@@ -460,7 +474,7 @@ private static byte[] init__JSON_value_from_state_actions_0()
static final int JSON_value_en_main = 1;
-// line 474 "Parser.rl"
+// line 488 "Parser.rl"
void parseValue(ParserResult res, int p, int pe) {
@@ -468,14 +482,14 @@ void parseValue(ParserResult res, int p, int pe) {
IRubyObject result = null;
-// line 472 "Parser.java"
+// line 486 "Parser.java"
{
cs = JSON_value_start;
}
-// line 481 "Parser.rl"
+// line 495 "Parser.rl"
-// line 479 "Parser.java"
+// line 493 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -501,13 +515,13 @@ void parseValue(ParserResult res, int p, int pe) {
while ( _nacts-- > 0 ) {
switch ( _JSON_value_actions[_acts++] ) {
case 9:
-// line 459 "Parser.rl"
+// line 473 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 511 "Parser.java"
+// line 525 "Parser.java"
}
}
@@ -570,25 +584,25 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
switch ( _JSON_value_actions[_acts++] )
{
case 0:
-// line 376 "Parser.rl"
+// line 390 "Parser.rl"
{
result = getRuntime().getNil();
}
break;
case 1:
-// line 379 "Parser.rl"
+// line 393 "Parser.rl"
{
result = getRuntime().getFalse();
}
break;
case 2:
-// line 382 "Parser.rl"
+// line 396 "Parser.rl"
{
result = getRuntime().getTrue();
}
break;
case 3:
-// line 385 "Parser.rl"
+// line 399 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_NAN);
@@ -598,7 +612,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 4:
-// line 392 "Parser.rl"
+// line 406 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_INFINITY);
@@ -608,7 +622,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 5:
-// line 399 "Parser.rl"
+// line 413 "Parser.rl"
{
if (pe > p + 8 &&
absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
@@ -637,7 +651,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 6:
-// line 425 "Parser.rl"
+// line 439 "Parser.rl"
{
parseString(res, p, pe);
if (res.result == null) {
@@ -650,7 +664,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 7:
-// line 435 "Parser.rl"
+// line 449 "Parser.rl"
{
currentNesting++;
parseArray(res, p, pe);
@@ -665,7 +679,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
break;
case 8:
-// line 447 "Parser.rl"
+// line 461 "Parser.rl"
{
currentNesting++;
parseObject(res, p, pe);
@@ -679,7 +693,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
}
break;
-// line 683 "Parser.java"
+// line 697 "Parser.java"
}
}
}
@@ -699,7 +713,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
break; }
}
-// line 482 "Parser.rl"
+// line 496 "Parser.rl"
if (cs >= JSON_value_first_final && result != null) {
if (parser.freeze) {
@@ -712,7 +726,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] )
}
-// line 716 "Parser.java"
+// line 730 "Parser.java"
private static byte[] init__JSON_integer_actions_0()
{
return new byte [] {
@@ -811,7 +825,7 @@ private static byte[] init__JSON_integer_trans_actions_0()
static final int JSON_integer_en_main = 1;
-// line 504 "Parser.rl"
+// line 518 "Parser.rl"
void parseInteger(ParserResult res, int p, int pe) {
@@ -829,15 +843,15 @@ int parseIntegerInternal(int p, int pe) {
int cs = EVIL;
-// line 833 "Parser.java"
+// line 847 "Parser.java"
{
cs = JSON_integer_start;
}
-// line 521 "Parser.rl"
+// line 535 "Parser.rl"
int memo = p;
-// line 841 "Parser.java"
+// line 855 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -918,13 +932,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] )
switch ( _JSON_integer_actions[_acts++] )
{
case 0:
-// line 498 "Parser.rl"
+// line 512 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 928 "Parser.java"
+// line 942 "Parser.java"
}
}
}
@@ -944,7 +958,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] )
break; }
}
-// line 523 "Parser.rl"
+// line 537 "Parser.rl"
if (cs < JSON_integer_first_final) {
return -1;
@@ -964,7 +978,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) {
}
-// line 968 "Parser.java"
+// line 982 "Parser.java"
private static byte[] init__JSON_float_actions_0()
{
return new byte [] {
@@ -1066,7 +1080,7 @@ private static byte[] init__JSON_float_trans_actions_0()
static final int JSON_float_en_main = 1;
-// line 556 "Parser.rl"
+// line 570 "Parser.rl"
void parseFloat(ParserResult res, int p, int pe) {
@@ -1085,15 +1099,15 @@ int parseFloatInternal(int p, int pe) {
int cs = EVIL;
-// line 1089 "Parser.java"
+// line 1103 "Parser.java"
{
cs = JSON_float_start;
}
-// line 574 "Parser.rl"
+// line 588 "Parser.rl"
int memo = p;
-// line 1097 "Parser.java"
+// line 1111 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1174,13 +1188,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
switch ( _JSON_float_actions[_acts++] )
{
case 0:
-// line 547 "Parser.rl"
+// line 561 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1184 "Parser.java"
+// line 1198 "Parser.java"
}
}
}
@@ -1200,7 +1214,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
break; }
}
-// line 576 "Parser.rl"
+// line 590 "Parser.rl"
if (cs < JSON_float_first_final) {
return -1;
@@ -1210,7 +1224,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] )
}
-// line 1214 "Parser.java"
+// line 1228 "Parser.java"
private static byte[] init__JSON_string_actions_0()
{
return new byte [] {
@@ -1312,7 +1326,7 @@ private static byte[] init__JSON_string_trans_actions_0()
static final int JSON_string_en_main = 1;
-// line 615 "Parser.rl"
+// line 629 "Parser.rl"
void parseString(ParserResult res, int p, int pe) {
@@ -1320,15 +1334,15 @@ void parseString(ParserResult res, int p, int pe) {
IRubyObject result = null;
-// line 1324 "Parser.java"
+// line 1338 "Parser.java"
{
cs = JSON_string_start;
}
-// line 622 "Parser.rl"
+// line 636 "Parser.rl"
int memo = p;
-// line 1332 "Parser.java"
+// line 1346 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1409,7 +1423,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
switch ( _JSON_string_actions[_acts++] )
{
case 0:
-// line 590 "Parser.rl"
+// line 604 "Parser.rl"
{
int offset = byteList.begin();
ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1424,13 +1438,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
}
break;
case 1:
-// line 603 "Parser.rl"
+// line 617 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1434 "Parser.java"
+// line 1448 "Parser.java"
}
}
}
@@ -1450,7 +1464,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] )
break; }
}
-// line 624 "Parser.rl"
+// line 638 "Parser.rl"
if (parser.createAdditions) {
RubyHash matchString = parser.match_string;
@@ -1471,6 +1485,9 @@ public void visit(IRubyObject pattern, IRubyObject klass) {
RubyClass klass = (RubyClass) memoArray[1];
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
+ if (parser.deprecatedCreateAdditions) {
+ klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
result = klass.callMethod(context, "json_create", result);
}
}
@@ -1495,7 +1512,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) {
}
-// line 1499 "Parser.java"
+// line 1516 "Parser.java"
private static byte[] init__JSON_array_actions_0()
{
return new byte [] {
@@ -1608,7 +1625,7 @@ private static byte[] init__JSON_array_trans_actions_0()
static final int JSON_array_en_main = 1;
-// line 704 "Parser.rl"
+// line 721 "Parser.rl"
void parseArray(ParserResult res, int p, int pe) {
@@ -1628,14 +1645,14 @@ void parseArray(ParserResult res, int p, int pe) {
}
-// line 1632 "Parser.java"
+// line 1649 "Parser.java"
{
cs = JSON_array_start;
}
-// line 723 "Parser.rl"
+// line 740 "Parser.rl"
-// line 1639 "Parser.java"
+// line 1656 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1716,7 +1733,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] )
switch ( _JSON_array_actions[_acts++] )
{
case 0:
-// line 673 "Parser.rl"
+// line 690 "Parser.rl"
{
parseValue(res, p, pe);
if (res.result == null) {
@@ -1733,13 +1750,13 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] )
}
break;
case 1:
-// line 688 "Parser.rl"
+// line 705 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1743 "Parser.java"
+// line 1760 "Parser.java"
}
}
}
@@ -1759,7 +1776,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] )
break; }
}
-// line 724 "Parser.rl"
+// line 741 "Parser.rl"
if (cs >= JSON_array_first_final) {
res.update(result, p + 1);
@@ -1769,7 +1786,7 @@ else if ( data[p] > _JSON_array_trans_keys[_mid+1] )
}
-// line 1773 "Parser.java"
+// line 1790 "Parser.java"
private static byte[] init__JSON_object_actions_0()
{
return new byte [] {
@@ -1892,7 +1909,7 @@ private static byte[] init__JSON_object_trans_actions_0()
static final int JSON_object_en_main = 1;
-// line 781 "Parser.rl"
+// line 798 "Parser.rl"
void parseObject(ParserResult res, int p, int pe) {
@@ -1917,14 +1934,14 @@ void parseObject(ParserResult res, int p, int pe) {
}
-// line 1921 "Parser.java"
+// line 1938 "Parser.java"
{
cs = JSON_object_start;
}
-// line 805 "Parser.rl"
+// line 822 "Parser.rl"
-// line 1928 "Parser.java"
+// line 1945 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2005,7 +2022,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
switch ( _JSON_object_actions[_acts++] )
{
case 0:
-// line 738 "Parser.rl"
+// line 755 "Parser.rl"
{
parseValue(res, p, pe);
if (res.result == null) {
@@ -2022,7 +2039,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
}
break;
case 1:
-// line 753 "Parser.rl"
+// line 770 "Parser.rl"
{
parseString(res, p, pe);
if (res.result == null) {
@@ -2040,13 +2057,13 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
}
break;
case 2:
-// line 769 "Parser.rl"
+// line 786 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 2050 "Parser.java"
+// line 2067 "Parser.java"
}
}
}
@@ -2066,7 +2083,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
break; }
}
-// line 806 "Parser.rl"
+// line 823 "Parser.rl"
if (cs < JSON_object_first_final) {
res.update(null, p + 1);
@@ -2090,6 +2107,9 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
callMethod(context, "deep_const_get", vKlassName);
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
+ if (parser.deprecatedCreateAdditions) {
+ klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
returnedResult = klass.callMethod(context, "json_create", result);
}
@@ -2099,7 +2119,7 @@ else if ( data[p] > _JSON_object_trans_keys[_mid+1] )
}
-// line 2103 "Parser.java"
+// line 2123 "Parser.java"
private static byte[] init__JSON_actions_0()
{
return new byte [] {
@@ -2202,7 +2222,7 @@ private static byte[] init__JSON_trans_actions_0()
static final int JSON_en_main = 1;
-// line 857 "Parser.rl"
+// line 877 "Parser.rl"
public IRubyObject parseImplemetation() {
@@ -2212,16 +2232,16 @@ public IRubyObject parseImplemetation() {
ParserResult res = new ParserResult();
-// line 2216 "Parser.java"
+// line 2236 "Parser.java"
{
cs = JSON_start;
}
-// line 866 "Parser.rl"
+// line 886 "Parser.rl"
p = byteList.begin();
pe = p + byteList.length();
-// line 2225 "Parser.java"
+// line 2245 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2302,7 +2322,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
switch ( _JSON_actions[_acts++] )
{
case 0:
-// line 843 "Parser.rl"
+// line 863 "Parser.rl"
{
parseValue(res, p, pe);
if (res.result == null) {
@@ -2314,7 +2334,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
}
}
break;
-// line 2318 "Parser.java"
+// line 2338 "Parser.java"
}
}
}
@@ -2334,7 +2354,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] )
break; }
}
-// line 869 "Parser.rl"
+// line 889 "Parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl
index 0fd6d7968..2173105f9 100644
--- a/java/src/json/ext/Parser.rl
+++ b/java/src/json/ext/Parser.rl
@@ -49,6 +49,7 @@ public class Parser extends RubyObject {
private RubyString vSource;
private RubyString createId;
private boolean createAdditions;
+ private boolean deprecatedCreateAdditions;
private int maxNesting;
private boolean allowNaN;
private boolean symbolizeNames;
@@ -169,7 +170,20 @@ public class Parser extends RubyObject {
this.symbolizeNames = opts.getBool("symbolize_names", false);
this.freeze = opts.getBool("freeze", false);
this.createId = opts.getString("create_id", getCreateId(context));
- this.createAdditions = opts.getBool("create_additions", false);
+
+ IRubyObject additions = opts.get("create_additions");
+ this.createAdditions = false;
+ this.deprecatedCreateAdditions = false;
+
+ if (additions != null) {
+ if (additions.isNil()) {
+ this.createAdditions = true;
+ this.deprecatedCreateAdditions = true;
+ } else {
+ this.createAdditions = opts.getBool("create_additions", false);
+ }
+ }
+
this.objectClass = opts.getClass("object_class", runtime.getHash());
this.arrayClass = opts.getClass("array_class", runtime.getArray());
this.decimalClass = opts.getClass("decimal_class", null);
@@ -641,6 +655,9 @@ public class Parser extends RubyObject {
RubyClass klass = (RubyClass) memoArray[1];
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
+ if (parser.deprecatedCreateAdditions) {
+ klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
result = klass.callMethod(context, "json_create", result);
}
}
@@ -826,6 +843,9 @@ public class Parser extends RubyObject {
callMethod(context, "deep_const_get", vKlassName);
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
+ if (parser.deprecatedCreateAdditions) {
+ klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`");
+ }
returnedResult = klass.callMethod(context, "json_create", result);
}
diff --git a/lib/json/common.rb b/lib/json/common.rb
index 182bb1c0e..2a2c076d4 100644
--- a/lib/json/common.rb
+++ b/lib/json/common.rb
@@ -49,18 +49,9 @@ def parser=(parser) # :nodoc:
# level (absolute namespace path?). If there doesn't exist a constant at
# the given path, an ArgumentError is raised.
def deep_const_get(path) # :nodoc:
- path.to_s.split(/::/).inject(Object) do |p, c|
- case
- when c.empty? then p
- when p.const_defined?(c, true) then p.const_get(c)
- else
- begin
- p.const_missing(c)
- rescue NameError => e
- raise ArgumentError, "can't get const #{path}: #{e}"
- end
- end
- end
+ Object.const_get(path)
+ rescue NameError => e
+ raise ArgumentError, "can't get const #{path}: #{e}"
end
# Set the module _generator_ to be used by JSON.
@@ -69,7 +60,7 @@ def generator=(generator) # :nodoc:
@generator = generator
generator_methods = generator::GeneratorMethods
for const in generator_methods.constants
- klass = deep_const_get(const)
+ klass = const_get(const)
modul = generator_methods.const_get(const)
klass.class_eval do
instance_methods(false).each do |m|
@@ -399,6 +390,20 @@ def pretty_generate(obj, opts = nil)
module_function :pretty_unparse
# :startdoc:
+ class << self
+ # Sets or returns default options for the JSON.unsafe_load method.
+ # Initially:
+ # opts = JSON.load_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
+ attr_accessor :unsafe_load_default_options
+ end
+ self.unsafe_load_default_options = {
+ :max_nesting => false,
+ :allow_nan => true,
+ :allow_blank => true,
+ :create_additions => true,
+ }
+
class << self
# Sets or returns default options for the JSON.load method.
# Initially:
@@ -407,11 +412,162 @@ class << self
attr_accessor :load_default_options
end
self.load_default_options = {
- :max_nesting => false,
:allow_nan => true,
:allow_blank => true,
- :create_additions => true,
+ :create_additions => nil,
}
+ # :call-seq:
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # - Argument +source+ must be, or be convertible to, a \String:
+ # - If +source+ responds to instance method +to_str+,
+ # source.to_str becomes the source.
+ # - If +source+ responds to instance method +to_io+,
+ # source.to_io.read becomes the source.
+ # - If +source+ responds to instance method +read+,
+ # source.read becomes the source.
+ # - If both of the following are true, source becomes the \String 'null':
+ # - Option +allow_blank+ specifies a truthy value.
+ # - The source, as defined above, is +nil+ or the empty \String ''.
+ # - Otherwise, +source+ remains the source.
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
+ # It will be called recursively with each result (depth-first order).
+ # See details below.
+ # BEWARE: This method is meant to serialise data from trusted user input,
+ # like from your own database server or clients under your control, it could
+ # be dangerous to allow untrusted users to pass JSON sources into it.
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
+ #
+ # ---
+ #
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
+ # parse(source, opts); see #parse.
+ #
+ # Source for following examples:
+ # source = <<~JSON
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # JSON
+ #
+ # Load a \String:
+ # ruby = JSON.unsafe_load(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load an \IO object:
+ # require 'stringio'
+ # object = JSON.unsafe_load(StringIO.new(source))
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load a \File object:
+ # path = 't.json'
+ # File.write(path, source)
+ # File.open(path) do |file|
+ # JSON.unsafe_load(file)
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # When +proc+ is given:
+ # - Modifies +source+ as above.
+ # - Gets the +result+ from calling parse(source, opts).
+ # - Recursively calls proc(result).
+ # - Returns the final result.
+ #
+ # Example:
+ # require 'json'
+ #
+ # # Some classes for the example.
+ # class Base
+ # def initialize(attributes)
+ # @attributes = attributes
+ # end
+ # end
+ # class User < Base; end
+ # class Account < Base; end
+ # class Admin < Base; end
+ # # The JSON source.
+ # json = <<-EOF
+ # {
+ # "users": [
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
+ # {"type": "User", "username": "john", "email": "john@example.com"}
+ # ],
+ # "accounts": [
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
+ # ],
+ # "admins": {"type": "Admin", "password": "0wn3d"}
+ # }
+ # EOF
+ # # Deserializer method.
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
+ # type = obj.is_a?(Hash) && obj["type"]
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
+ # end
+ # # Call to JSON.unsafe_load
+ # ruby = JSON.unsafe_load(json, proc {|obj|
+ # case obj
+ # when Hash
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
+ # when Array
+ # obj.map! {|v| deserialize_obj v }
+ # end
+ # })
+ # pp ruby
+ # Output:
+ # {"users"=>
+ # [#"User", "username"=>"jane", "email"=>"jane@example.com"}>,
+ # #"User", "username"=>"john", "email"=>"john@example.com"}>],
+ # "accounts"=>
+ # [{"account"=>
+ # #"Account", "paid"=>true, "account_id"=>"1234"}>},
+ # {"account"=>
+ # #"Account", "paid"=>false, "account_id"=>"1235"}>}],
+ # "admins"=>
+ # #"Admin", "password"=>"0wn3d"}>}
+ #
+ def unsafe_load(source, proc = nil, options = nil)
+ opts = if options.nil?
+ unsafe_load_default_options
+ else
+ unsafe_load_default_options.merge(options)
+ end
+
+ unless source.is_a?(String)
+ if source.respond_to? :to_str
+ source = source.to_str
+ elsif source.respond_to? :to_io
+ source = source.to_io.read
+ elsif source.respond_to?(:read)
+ source = source.read
+ end
+ end
+
+ if opts[:allow_blank] && (source.nil? || source.empty?)
+ source = 'null'
+ end
+ result = parse(source, opts)
+ recurse_proc(result, &proc) if proc
+ result
+ end
# :call-seq:
# JSON.load(source, proc = nil, options = {}) -> object
@@ -435,6 +591,7 @@ class << self
# BEWARE: This method is meant to serialise data from trusted user input,
# like from your own database server or clients under your control, it could
# be dangerous to allow untrusted users to pass JSON sources into it.
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
# The default options can be changed via method JSON.load_default_options=.
diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb
index 1afd8e466..b372478b7 100644
--- a/lib/json/pure/parser.rb
+++ b/lib/json/pure/parser.rb
@@ -90,11 +90,14 @@ def initialize(source, opts = nil)
@allow_nan = !!opts[:allow_nan]
@symbolize_names = !!opts[:symbolize_names]
@freeze = !!opts[:freeze]
- if opts.key?(:create_additions)
- @create_additions = !!opts[:create_additions]
- else
- @create_additions = false
+
+ @deprecated_create_additions = false
+ @create_additions = opts.fetch(:create_additions, false)
+ if @create_additions.nil?
+ @create_additions = true
+ @deprecated_create_additions = true
end
+
@symbolize_names && @create_additions and raise ArgumentError,
'options :symbolize_names and :create_additions cannot be used '\
'in conjunction'
@@ -185,8 +188,13 @@ def parse_string
if @create_additions and @match_string
for (regexp, klass) in @match_string
- klass.json_creatable? or next
- string =~ regexp and return klass.json_create(string)
+ if klass.json_creatable? and string.match?(regexp)
+ if @deprecated_create_additions
+ warn "JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"
+ end
+
+ return klass.json_create(string)
+ end
end
end
string
@@ -308,8 +316,11 @@ def parse_object
raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
end
if @create_additions and klassname = result[@create_id]
- klass = JSON.deep_const_get klassname
+ klass = JSON.deep_const_get(klassname)
break unless klass and klass.json_creatable?
+ if @deprecated_create_additions
+ warn "JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"
+ end
result = klass.json_create(result)
end
break
diff --git a/test/json/json_addition_test.rb b/test/json/json_addition_test.rb
index 8c3fbda56..d78ae55c3 100644
--- a/test/json/json_addition_test.rb
+++ b/test/json/json_addition_test.rb
@@ -162,6 +162,12 @@ def test_core
assert_equal(/foo/i, JSON(JSON(/foo/i), :create_additions => true))
end
+ def test_deprecated_load_create_additions
+ assert_warning(/use JSON\.unsafe_load/) do
+ JSON.load(JSON.dump(Time.now))
+ end
+ end
+
def test_utc_datetime
now = Time.now
d = DateTime.parse(now.to_s) # usual case
diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb
index 01e412c96..68d3e9702 100755
--- a/test/json/json_generator_test.rb
+++ b/test/json/json_generator_test.rb
@@ -280,7 +280,7 @@ def test_gc
end
EOS
end
- end if GC.respond_to?(:stress=)
+ end if GC.respond_to?(:stress=) && RUBY_ENGINE == "ruby"
def test_configure_using_configure_and_merge
numbered_state = {