diff options
31 files changed, 1254 insertions, 906 deletions
diff --git a/.travis.yml b/.travis.yml index e1ebeda..a8f9071 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,11 @@ language: ruby # Specify which ruby versions you wish to run your tests on, each version will be used rvm: - - 1.8.7 - - 1.9.2 - 1.9.3 - 2.0.0 - 2.1 - 2.2 - - 2.3.0 - - ree + - 2.3.1 - ruby-head matrix: include: @@ -5,3 +5,5 @@ source 'https://rubygems.org' gemspec :name => 'json' gemspec :name => 'json_pure' gemspec :name => 'json-java' + +gem 'simplecov' @@ -26,13 +26,6 @@ encoded, please use the to\_json\_raw\_object method of String (which produces an object, that contains a byte array) and decode the result on the receiving endpoint. -The JSON parsers can parse UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and UTF-32LE -JSON documents under Ruby 1.8. Under Ruby 1.9 they take advantage of Ruby's -M17n features and can parse all documents which have the correct -String#encoding set. If a document string has ASCII-8BIT as an encoding the -parser attempts to figure out which of the UTF encodings from above it is and -trys to parse it. - ## Installation It's recommended to use the extension variant of JSON, because it's faster than @@ -117,11 +110,7 @@ You can also use the `pretty_generate` method (which formats the output more verbosely and nicely) or `fast_generate` (which doesn't do any of the security checks generate performs, e. g. nesting deepness checks). -To create a valid JSON document you have to make sure, that the output is -embedded in either a JSON array `[]` or a JSON object `{}`. The easiest way to do -this, is by putting your values in a Ruby Array or Hash instance. - -There are also the `JSON` and `JSON[]` methods which use parse on a String or +There are also the JSON and JSON[] methods which use parse on a String or generate a JSON document from an array or hash: ```ruby @@ -156,6 +156,7 @@ task :version do puts m File.open(File.join('lib', 'json', 'version.rb'), 'w') do |v| v.puts <<EOT +# frozen_string_literal: false module JSON # JSON version VERSION = '#{PKG_VERSION}' @@ -1 +0,0 @@ - diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 393e29c..2a3db29 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -14,7 +14,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, - i_quirks_mode, i_pack, i_unpack, i_create_id, i_extend, i_key_p, + i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, i_buffer_initial_length, i_dup; @@ -622,8 +622,6 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->allow_nan = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); state->ascii_only = RTEST(tmp); - tmp = rb_hash_aref(opts, ID2SYM(i_quirks_mode)); - state->quirks_mode = RTEST(tmp); return self; } @@ -657,7 +655,6 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len)); rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); - rb_hash_aset(result, ID2SYM(i_quirks_mode), state->quirks_mode ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length)); @@ -943,8 +940,6 @@ static VALUE cState_generate(VALUE self, VALUE obj) * * *allow_nan*: true if NaN, Infinity, and -Infinity should be * generated, otherwise an exception is thrown, if these values are * encountered. This options defaults to false. - * * *quirks_mode*: Enables quirks_mode for parser, that is for example - * generating single JSON values instead of documents is possible. * * *buffer_initial_length*: sets the initial length of the generator's * internal buffer. */ @@ -1252,29 +1247,6 @@ static VALUE cState_ascii_only_p(VALUE self) } /* - * call-seq: quirks_mode? - * - * Returns true, if quirks mode is enabled. Otherwise returns false. - */ -static VALUE cState_quirks_mode_p(VALUE self) -{ - GET_STATE(self); - return state->quirks_mode ? Qtrue : Qfalse; -} - -/* - * call-seq: quirks_mode=(enable) - * - * If set to true, enables the quirks_mode mode. - */ -static VALUE cState_quirks_mode_set(VALUE self, VALUE enable) -{ - GET_STATE(self); - state->quirks_mode = RTEST(enable); - return Qnil; -} - -/* * call-seq: depth * * This integer returns the current depth of data structure nesting. @@ -1362,9 +1334,6 @@ void Init_generator(void) rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); - rb_define_method(cState, "quirks_mode?", cState_quirks_mode_p, 0); - rb_define_method(cState, "quirks_mode", cState_quirks_mode_p, 0); - rb_define_method(cState, "quirks_mode=", cState_quirks_mode_set, 1); rb_define_method(cState, "depth", cState_depth, 0); rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0); @@ -1416,7 +1385,6 @@ void Init_generator(void) i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); - i_quirks_mode = rb_intern("quirks_mode"); i_depth = rb_intern("depth"); i_buffer_initial_length = rb_intern("buffer_initial_length"); i_pack = rb_intern("pack"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index 298c0a4..7432f26 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -73,7 +73,6 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; - char quirks_mode; long depth; long buffer_initial_length; } JSON_Generator_State; diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 9360247..71292e4 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -89,6 +89,11 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) return len; } +#ifdef HAVE_RUBY_ENCODING_H +static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8; +static ID i_encoding, i_encode; +#endif + static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; @@ -98,11 +103,11 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_match_string, i_aset, i_aref, i_leftshift; -#line 124 "parser.rl" +#line 129 "parser.rl" -#line 106 "parser.c" +#line 111 "parser.c" enum {JSON_object_start = 1}; enum {JSON_object_first_final = 27}; enum {JSON_object_error = 0}; @@ -110,7 +115,7 @@ enum {JSON_object_error = 0}; enum {JSON_object_en_main = 1}; -#line 165 "parser.rl" +#line 170 "parser.rl" static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -126,14 +131,14 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); -#line 130 "parser.c" +#line 135 "parser.c" { cs = JSON_object_start; } -#line 180 "parser.rl" +#line 185 "parser.rl" -#line 137 "parser.c" +#line 142 "parser.c" { if ( p == pe ) goto _test_eof; @@ -161,7 +166,7 @@ case 2: goto st2; goto st0; tr2: -#line 147 "parser.rl" +#line 152 "parser.rl" { char *np; json->parsing_name = 1; @@ -174,7 +179,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 178 "parser.c" +#line 183 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -241,7 +246,7 @@ case 8: goto st8; goto st0; tr11: -#line 132 "parser.rl" +#line 137 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v); @@ -261,7 +266,7 @@ st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 265 "parser.c" +#line 270 "parser.c" switch( (*p) ) { case 13: goto st9; case 32: goto st9; @@ -350,14 +355,14 @@ case 18: goto st9; goto st18; tr4: -#line 155 "parser.rl" +#line 160 "parser.rl" { p--; {p++; cs = 27; goto _out;} } goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 361 "parser.c" +#line 366 "parser.c" goto st0; st19: if ( ++p == pe ) @@ -455,7 +460,7 @@ case 26: _out: {} } -#line 181 "parser.rl" +#line 186 "parser.rl" if (cs >= JSON_object_first_final) { if (json->create_additions) { @@ -480,7 +485,7 @@ case 26: -#line 484 "parser.c" +#line 489 "parser.c" enum {JSON_value_start = 1}; enum {JSON_value_first_final = 29}; enum {JSON_value_error = 0}; @@ -488,7 +493,7 @@ enum {JSON_value_error = 0}; enum {JSON_value_en_main = 1}; -#line 285 "parser.rl" +#line 290 "parser.rl" static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -496,14 +501,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 500 "parser.c" +#line 505 "parser.c" { cs = JSON_value_start; } -#line 292 "parser.rl" +#line 297 "parser.rl" -#line 507 "parser.c" +#line 512 "parser.c" { if ( p == pe ) goto _test_eof; @@ -537,14 +542,14 @@ st0: cs = 0; goto _out; tr2: -#line 233 "parser.rl" +#line 238 "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 238 "parser.rl" +#line 243 "parser.rl" { char *np; if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) { @@ -564,7 +569,7 @@ tr3: } goto st29; tr7: -#line 256 "parser.rl" +#line 261 "parser.rl" { char *np; json->current_nesting++; @@ -574,7 +579,7 @@ tr7: } goto st29; tr11: -#line 264 "parser.rl" +#line 269 "parser.rl" { char *np; json->current_nesting++; @@ -584,7 +589,7 @@ tr11: } goto st29; tr25: -#line 226 "parser.rl" +#line 231 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -594,7 +599,7 @@ tr25: } goto st29; tr27: -#line 219 "parser.rl" +#line 224 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -604,19 +609,19 @@ tr27: } goto st29; tr31: -#line 213 "parser.rl" +#line 218 "parser.rl" { *result = Qfalse; } goto st29; tr34: -#line 210 "parser.rl" +#line 215 "parser.rl" { *result = Qnil; } goto st29; tr37: -#line 216 "parser.rl" +#line 221 "parser.rl" { *result = Qtrue; } @@ -625,9 +630,9 @@ st29: if ( ++p == pe ) goto _test_eof29; case 29: -#line 272 "parser.rl" +#line 277 "parser.rl" { p--; {p++; cs = 29; goto _out;} } -#line 631 "parser.c" +#line 636 "parser.c" switch( (*p) ) { case 13: goto st29; case 32: goto st29; @@ -868,7 +873,7 @@ case 28: _out: {} } -#line 293 "parser.rl" +#line 298 "parser.rl" if (cs >= JSON_value_first_final) { return p; @@ -878,7 +883,7 @@ case 28: } -#line 882 "parser.c" +#line 887 "parser.c" enum {JSON_integer_start = 1}; enum {JSON_integer_first_final = 3}; enum {JSON_integer_error = 0}; @@ -886,7 +891,7 @@ enum {JSON_integer_error = 0}; enum {JSON_integer_en_main = 1}; -#line 309 "parser.rl" +#line 314 "parser.rl" static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -894,15 +899,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res int cs = EVIL; -#line 898 "parser.c" +#line 903 "parser.c" { cs = JSON_integer_start; } -#line 316 "parser.rl" +#line 321 "parser.rl" json->memo = p; -#line 906 "parser.c" +#line 911 "parser.c" { if ( p == pe ) goto _test_eof; @@ -936,14 +941,14 @@ case 3: goto st0; goto tr4; tr4: -#line 306 "parser.rl" +#line 311 "parser.rl" { p--; {p++; cs = 4; goto _out;} } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 947 "parser.c" +#line 952 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -962,7 +967,7 @@ case 5: _out: {} } -#line 318 "parser.rl" +#line 323 "parser.rl" if (cs >= JSON_integer_first_final) { long len = p - json->memo; @@ -977,7 +982,7 @@ case 5: } -#line 981 "parser.c" +#line 986 "parser.c" enum {JSON_float_start = 1}; enum {JSON_float_first_final = 8}; enum {JSON_float_error = 0}; @@ -985,7 +990,7 @@ enum {JSON_float_error = 0}; enum {JSON_float_en_main = 1}; -#line 343 "parser.rl" +#line 348 "parser.rl" static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -993,15 +998,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul int cs = EVIL; -#line 997 "parser.c" +#line 1002 "parser.c" { cs = JSON_float_start; } -#line 350 "parser.rl" +#line 355 "parser.rl" json->memo = p; -#line 1005 "parser.c" +#line 1010 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1059,14 +1064,14 @@ case 8: goto st0; goto tr9; tr9: -#line 337 "parser.rl" +#line 342 "parser.rl" { p--; {p++; cs = 9; goto _out;} } goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 1070 "parser.c" +#line 1075 "parser.c" goto st0; st5: if ( ++p == pe ) @@ -1127,7 +1132,7 @@ case 7: _out: {} } -#line 352 "parser.rl" +#line 357 "parser.rl" if (cs >= JSON_float_first_final) { long len = p - json->memo; @@ -1143,7 +1148,7 @@ case 7: -#line 1147 "parser.c" +#line 1152 "parser.c" enum {JSON_array_start = 1}; enum {JSON_array_first_final = 17}; enum {JSON_array_error = 0}; @@ -1151,7 +1156,7 @@ enum {JSON_array_error = 0}; enum {JSON_array_en_main = 1}; -#line 395 "parser.rl" +#line 400 "parser.rl" static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result) @@ -1165,14 +1170,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 1169 "parser.c" +#line 1174 "parser.c" { cs = JSON_array_start; } -#line 408 "parser.rl" +#line 413 "parser.rl" -#line 1176 "parser.c" +#line 1181 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1211,7 +1216,7 @@ case 2: goto st2; goto st0; tr2: -#line 372 "parser.rl" +#line 377 "parser.rl" { VALUE v = Qnil; char *np = JSON_parse_value(json, p, pe, &v); @@ -1231,7 +1236,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1235 "parser.c" +#line 1240 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1331,14 +1336,14 @@ case 12: goto st3; goto st12; tr4: -#line 387 "parser.rl" +#line 392 "parser.rl" { p--; {p++; cs = 17; goto _out;} } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1342 "parser.c" +#line 1347 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1394,7 +1399,7 @@ case 16: _out: {} } -#line 409 "parser.rl" +#line 414 "parser.rl" if(cs >= JSON_array_first_final) { return p + 1; @@ -1475,7 +1480,7 @@ static VALUE json_string_unescape(VALUE result, char *string, char *stringEnd) } -#line 1479 "parser.c" +#line 1484 "parser.c" enum {JSON_string_start = 1}; enum {JSON_string_first_final = 8}; enum {JSON_string_error = 0}; @@ -1483,7 +1488,7 @@ enum {JSON_string_error = 0}; enum {JSON_string_en_main = 1}; -#line 508 "parser.rl" +#line 513 "parser.rl" static int @@ -1505,15 +1510,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu *result = rb_str_buf_new(0); -#line 1509 "parser.c" +#line 1514 "parser.c" { cs = JSON_string_start; } -#line 529 "parser.rl" +#line 534 "parser.rl" json->memo = p; -#line 1517 "parser.c" +#line 1522 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1538,7 +1543,7 @@ case 2: goto st0; goto st2; tr2: -#line 494 "parser.rl" +#line 499 "parser.rl" { *result = json_string_unescape(*result, json->memo + 1, p); if (NIL_P(*result)) { @@ -1549,14 +1554,14 @@ tr2: {p = (( p + 1))-1;} } } -#line 505 "parser.rl" +#line 510 "parser.rl" { p--; {p++; cs = 8; goto _out;} } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1560 "parser.c" +#line 1565 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1632,7 +1637,7 @@ case 7: _out: {} } -#line 531 "parser.rl" +#line 536 "parser.rl" if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; @@ -1670,9 +1675,7 @@ case 7: static VALUE convert_encoding(VALUE source) { #ifdef HAVE_RUBY_ENCODING_H - { - source = rb_str_conv_enc(source, NULL, rb_utf8_encoding()); - } + source = rb_str_conv_enc(source, NULL, rb_utf8_encoding()); #endif return source; } @@ -1805,7 +1808,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } -#line 1809 "parser.c" +#line 1812 "parser.c" enum {JSON_start = 1}; enum {JSON_first_final = 10}; enum {JSON_error = 0}; @@ -1813,7 +1816,7 @@ enum {JSON_error = 0}; enum {JSON_en_main = 1}; -#line 717 "parser.rl" +#line 720 "parser.rl" /* @@ -1830,16 +1833,16 @@ static VALUE cParser_parse(VALUE self) GET_PARSER; -#line 1834 "parser.c" +#line 1837 "parser.c" { cs = JSON_start; } -#line 733 "parser.rl" +#line 736 "parser.rl" p = json->source; pe = p + json->len; -#line 1843 "parser.c" +#line 1846 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1873,7 +1876,7 @@ st0: cs = 0; goto _out; tr2: -#line 709 "parser.rl" +#line 712 "parser.rl" { char *np = JSON_parse_value(json, p, pe, &result); if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} @@ -1883,7 +1886,7 @@ st10: if ( ++p == pe ) goto _test_eof10; case 10: -#line 1887 "parser.c" +#line 1890 "parser.c" switch( (*p) ) { case 13: goto st10; case 32: goto st10; @@ -1972,7 +1975,7 @@ case 9: _out: {} } -#line 736 "parser.rl" +#line 739 "parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -2070,6 +2073,12 @@ void Init_parser(void) i_aset = rb_intern("[]="); i_aref = rb_intern("[]"); i_leftshift = rb_intern("<<"); +#ifdef HAVE_RUBY_ENCODING_H + CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); + CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit")); + i_encoding = rb_intern("encoding"); + i_encode = rb_intern("encode"); +#endif } /* diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index cc7e164..e8ad426 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -87,6 +87,11 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch) return len; } +#ifdef HAVE_RUBY_ENCODING_H +static VALUE CEncoding_ASCII_8BIT, CEncoding_UTF_8; +static ID i_encoding, i_encode; +#endif + static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; @@ -565,9 +570,7 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu static VALUE convert_encoding(VALUE source) { #ifdef HAVE_RUBY_ENCODING_H - { - source = rb_str_conv_enc(source, NULL, rb_utf8_encoding()); - } + source = rb_str_conv_enc(source, NULL, rb_utf8_encoding()); #endif return source; } @@ -830,6 +833,12 @@ void Init_parser(void) i_aset = rb_intern("[]="); i_aref = rb_intern("[]"); i_leftshift = rb_intern("<<"); +#ifdef HAVE_RUBY_ENCODING_H + CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8")); + CEncoding_ASCII_8BIT = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("ascii-8bit")); + i_encoding = rb_intern("encoding"); + i_encode = rb_intern("encode"); +#endif } /* diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index bb3a394..96a5e7e 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -172,9 +172,7 @@ public final class Generator { result = RubyString.newString(session.getRuntime(), buffer); ThreadContext context = session.getContext(); RuntimeInfo info = session.getInfo(); - if (info.encodingsSupported()) { - result.force_encoding(context, info.utf8.get()); - } + result.force_encoding(context, info.utf8.get()); return result; } @@ -381,8 +379,7 @@ public final class Generator { RuntimeInfo info = session.getInfo(); RubyString src; - if (info.encodingsSupported() && - object.encoding(session.getContext()) != info.utf8.get()) { + if (object.encoding(session.getContext()) != info.utf8.get()) { src = (RubyString)object.encode(session.getContext(), info.utf8.get()); } else { diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 11d98d0..44a9311 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -208,9 +208,7 @@ public class GeneratorState extends RubyObject { public IRubyObject generate(ThreadContext context, IRubyObject obj) { RubyString result = Generator.generateJson(context, obj, this); RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); - if (info.encodingsSupported()) { - result.force_encoding(context, info.utf8.get()); - } + result.force_encoding(context, info.utf8.get()); return result; } @@ -366,17 +364,6 @@ public class GeneratorState extends RubyObject { return context.getRuntime().newBoolean(asciiOnly); } - @JRubyMethod(name="quirks_mode") - public RubyBoolean quirks_mode_get(ThreadContext context) { - return context.getRuntime().newBoolean(quirksMode); - } - - @JRubyMethod(name="quirks_mode=") - public IRubyObject quirks_mode_set(IRubyObject quirks_mode) { - quirksMode = quirks_mode.isTrue(); - return quirks_mode.getRuntime().newBoolean(quirksMode); - } - @JRubyMethod(name="buffer_initial_length") public RubyInteger buffer_initial_length_get(ThreadContext context) { return context.getRuntime().newFixnum(bufferInitialLength); @@ -389,11 +376,6 @@ public class GeneratorState extends RubyObject { return buffer_initial_length; } - @JRubyMethod(name="quirks_mode?") - public RubyBoolean quirks_mode_p(ThreadContext context) { - return context.getRuntime().newBoolean(quirksMode); - } - public int getDepth() { return depth; } @@ -412,7 +394,7 @@ public class GeneratorState extends RubyObject { private ByteList prepareByteList(ThreadContext context, IRubyObject value) { RubyString str = value.convertToString(); RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); - if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) { + if (str.encoding(context) != info.utf8.get()) { str = (RubyString)str.encode(context, info.utf8.get()); } return str.getByteList().dup(); @@ -448,7 +430,6 @@ public class GeneratorState extends RubyObject { maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); - quirksMode = opts.getBool("quirks_mode", DEFAULT_QUIRKS_MODE); bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH); depth = opts.getInt("depth", 0); @@ -475,7 +456,6 @@ public class GeneratorState extends RubyObject { result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context)); result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context)); result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context)); - result.op_aset(context, runtime.newSymbol("quirks_mode"), quirks_mode_p(context)); result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context)); diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java index 9bb6e64..70426d4 100644 --- a/java/src/json/ext/OptionsReader.java +++ b/java/src/json/ext/OptionsReader.java @@ -84,7 +84,7 @@ final class OptionsReader { RubyString str = value.convertToString(); RuntimeInfo info = getRuntimeInfo(); - if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) { + if (str.encoding(context) != info.utf8.get()) { str = (RubyString)str.encode(context, info.utf8.get()); } return str; diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 5458fb1..8c33840 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -52,10 +52,9 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; - private boolean quirksMode; private RubyClass objectClass; private RubyClass arrayClass; - private RubyHash match_string; + private RubyHash matchString; private static final int DEFAULT_MAX_NESTING = 100; @@ -123,10 +122,6 @@ public class Parser extends RubyObject { * <dd>If set to <code>true</code>, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. * - * <dt><code>:quirks_mode?</code> - * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false - * otherwise. - * * <dt><code>:create_additions</code> * <dd>If set to <code>false</code>, the Parser doesn't create additions * even if a matching class and <code>create_id</code> was found. This option @@ -138,9 +133,6 @@ public class Parser extends RubyObject { * <dt><code>:array_class</code> * <dd>Defaults to Array. * - * <dt><code>:quirks_mode</code> - * <dd>Enables quirks_mode for parser, that is for example parsing single - * JSON values instead of documents is possible. * </dl> */ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) @@ -163,15 +155,20 @@ public class Parser extends RubyObject { this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); - this.quirksMode = opts.getBool("quirks_mode", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", false); this.objectClass = opts.getClass("object_class", runtime.getHash()); this.arrayClass = opts.getClass("array_class", runtime.getArray()); - this.match_string = opts.getHash("match_string"); + this.matchString = opts.getHash("match_string"); + if(symbolizeNames && createAdditions) { + throw runtime.newArgumentError( + "options :symbolize_names and :create_additions cannot be " + + " used in conjunction" + ); + } this.vSource = args[0].convertToString(); - if (!quirksMode) this.vSource = convertEncoding(context, vSource); + this.vSource = convertEncoding(context, vSource); return this; } @@ -182,33 +179,13 @@ public class Parser extends RubyObject { * Returns the source string if no conversion is needed. */ private RubyString convertEncoding(ThreadContext context, RubyString source) { - ByteList bl = source.getByteList(); - int len = bl.length(); - if (len < 2) { - throw Utils.newException(context, Utils.M_PARSER_ERROR, - "A JSON text must at least contain two octets!"); - } - - if (info.encodingsSupported()) { - RubyEncoding encoding = (RubyEncoding)source.encoding(context); - if (encoding != info.ascii8bit.get()) { - return (RubyString)source.encode(context, info.utf8.get()); - } - - String sniffedEncoding = sniffByteList(bl); - if (sniffedEncoding == null) return source; // assume UTF-8 - return reinterpretEncoding(context, source, sniffedEncoding); - } - - String sniffedEncoding = sniffByteList(bl); - if (sniffedEncoding == null) return source; // assume UTF-8 - Ruby runtime = context.getRuntime(); - return (RubyString)info.jsonModule.get(). - callMethod(context, "iconv", - new IRubyObject[] { - runtime.newString("utf-8"), - runtime.newString(sniffedEncoding), - source}); + RubyEncoding encoding = (RubyEncoding)source.encoding(context); + if (encoding == info.ascii8bit.get()) { + source.force_encoding(context, info.utf8.get()); + } else { + source = (RubyString) source.encode(context, info.utf8.get()); + } + return source; } /** @@ -261,17 +238,6 @@ public class Parser extends RubyObject { return checkAndGetSource().dup(); } - /** - * <code>Parser#quirks_mode?()</code> - * - * <p>If set to <code>true</code>, if the parse is in quirks_mode, false - * otherwise. - */ - @JRubyMethod(name = "quirks_mode?") - public IRubyObject quirks_mode_p(ThreadContext context) { - return context.getRuntime().newBoolean(quirksMode); - } - public RubyString checkAndGetSource() { if (vSource != null) { return vSource; @@ -338,11 +304,11 @@ public class Parser extends RubyObject { } -// line 365 "Parser.rl" +// line 330 "Parser.rl" -// line 347 "Parser.java" +// line 312 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -456,7 +422,7 @@ static final int JSON_value_error = 0; static final int JSON_value_en_main = 1; -// line 471 "Parser.rl" +// line 436 "Parser.rl" void parseValue(ParserResult res, int p, int pe) { @@ -464,14 +430,14 @@ static final int JSON_value_en_main = 1; IRubyObject result = null; -// line 469 "Parser.java" +// line 434 "Parser.java" { cs = JSON_value_start; } -// line 478 "Parser.rl" +// line 443 "Parser.rl" -// line 476 "Parser.java" +// line 441 "Parser.java" { int _klen; int _trans = 0; @@ -497,13 +463,13 @@ case 1: while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 456 "Parser.rl" +// line 421 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 508 "Parser.java" +// line 473 "Parser.java" } } @@ -566,25 +532,25 @@ case 1: switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 373 "Parser.rl" +// line 338 "Parser.rl" { result = getRuntime().getNil(); } break; case 1: -// line 376 "Parser.rl" +// line 341 "Parser.rl" { result = getRuntime().getFalse(); } break; case 2: -// line 379 "Parser.rl" +// line 344 "Parser.rl" { result = getRuntime().getTrue(); } break; case 3: -// line 382 "Parser.rl" +// line 347 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); @@ -594,7 +560,7 @@ case 1: } break; case 4: -// line 389 "Parser.rl" +// line 354 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); @@ -604,9 +570,9 @@ case 1: } break; case 5: -// line 396 "Parser.rl" +// line 361 "Parser.rl" { - if (pe > p + 9 - (parser.quirksMode ? 1 : 0) && + if (pe > p + 8 && absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) { if (parser.allowNaN) { @@ -633,7 +599,7 @@ case 1: } break; case 6: -// line 422 "Parser.rl" +// line 387 "Parser.rl" { parseString(res, p, pe); if (res.result == null) { @@ -646,7 +612,7 @@ case 1: } break; case 7: -// line 432 "Parser.rl" +// line 397 "Parser.rl" { currentNesting++; parseArray(res, p, pe); @@ -661,7 +627,7 @@ case 1: } break; case 8: -// line 444 "Parser.rl" +// line 409 "Parser.rl" { currentNesting++; parseObject(res, p, pe); @@ -675,7 +641,7 @@ case 1: } } break; -// line 680 "Parser.java" +// line 645 "Parser.java" } } } @@ -695,7 +661,7 @@ case 5: break; } } -// line 479 "Parser.rl" +// line 444 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { res.update(result, p); @@ -705,7 +671,7 @@ case 5: } -// line 710 "Parser.java" +// line 675 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -804,7 +770,7 @@ static final int JSON_integer_error = 0; static final int JSON_integer_en_main = 1; -// line 498 "Parser.rl" +// line 463 "Parser.rl" void parseInteger(ParserResult res, int p, int pe) { @@ -822,15 +788,15 @@ static final int JSON_integer_en_main = 1; int cs = EVIL; -// line 827 "Parser.java" +// line 792 "Parser.java" { cs = JSON_integer_start; } -// line 515 "Parser.rl" +// line 480 "Parser.rl" int memo = p; -// line 835 "Parser.java" +// line 800 "Parser.java" { int _klen; int _trans = 0; @@ -911,13 +877,13 @@ case 1: switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 492 "Parser.rl" +// line 457 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 922 "Parser.java" +// line 887 "Parser.java" } } } @@ -937,7 +903,7 @@ case 5: break; } } -// line 517 "Parser.rl" +// line 482 "Parser.rl" if (cs < JSON_integer_first_final) { return -1; @@ -959,7 +925,7 @@ case 5: } -// line 964 "Parser.java" +// line 929 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -1061,7 +1027,7 @@ static final int JSON_float_error = 0; static final int JSON_float_en_main = 1; -// line 552 "Parser.rl" +// line 517 "Parser.rl" void parseFloat(ParserResult res, int p, int pe) { @@ -1079,15 +1045,15 @@ static final int JSON_float_en_main = 1; int cs = EVIL; -// line 1084 "Parser.java" +// line 1049 "Parser.java" { cs = JSON_float_start; } -// line 569 "Parser.rl" +// line 534 "Parser.rl" int memo = p; -// line 1092 "Parser.java" +// line 1057 "Parser.java" { int _klen; int _trans = 0; @@ -1168,13 +1134,13 @@ case 1: switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 543 "Parser.rl" +// line 508 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1179 "Parser.java" +// line 1144 "Parser.java" } } } @@ -1194,7 +1160,7 @@ case 5: break; } } -// line 571 "Parser.rl" +// line 536 "Parser.rl" if (cs < JSON_float_first_final) { return -1; @@ -1210,7 +1176,7 @@ case 5: } -// line 1215 "Parser.java" +// line 1180 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1312,7 +1278,7 @@ static final int JSON_string_error = 0; static final int JSON_string_en_main = 1; -// line 616 "Parser.rl" +// line 581 "Parser.rl" void parseString(ParserResult res, int p, int pe) { @@ -1320,15 +1286,15 @@ static final int JSON_string_en_main = 1; IRubyObject result = null; -// line 1325 "Parser.java" +// line 1290 "Parser.java" { cs = JSON_string_start; } -// line 623 "Parser.rl" +// line 588 "Parser.rl" int memo = p; -// line 1333 "Parser.java" +// line 1298 "Parser.java" { int _klen; int _trans = 0; @@ -1409,7 +1375,7 @@ case 1: switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 591 "Parser.rl" +// line 556 "Parser.rl" { int offset = byteList.begin(); ByteList decoded = decoder.decode(byteList, memo + 1 - offset, @@ -1424,13 +1390,13 @@ case 1: } break; case 1: -// line 604 "Parser.rl" +// line 569 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1435 "Parser.java" +// line 1400 "Parser.java" } } } @@ -1450,14 +1416,14 @@ case 5: break; } } -// line 625 "Parser.rl" +// line 590 "Parser.rl" if (parser.createAdditions) { - RubyHash match_string = parser.match_string; - if (match_string != null) { + RubyHash matchString = parser.matchString; + if (matchString != null) { final IRubyObject[] memoArray = { result, null }; try { - match_string.visitAll(new RubyHash.Visitor() { + matchString.visitAll(new RubyHash.Visitor() { @Override public void visit(IRubyObject pattern, IRubyObject klass) { if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) { @@ -1478,7 +1444,7 @@ case 5: } if (cs >= JSON_string_first_final && result != null) { - if (info.encodingsSupported() && result instanceof RubyString) { + if (result instanceof RubyString) { ((RubyString)result).force_encoding(context, info.utf8.get()); } res.update(result, p + 1); @@ -1488,7 +1454,7 @@ case 5: } -// line 1493 "Parser.java" +// line 1458 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1601,7 +1567,7 @@ static final int JSON_array_error = 0; static final int JSON_array_en_main = 1; -// line 698 "Parser.rl" +// line 663 "Parser.rl" void parseArray(ParserResult res, int p, int pe) { @@ -1621,14 +1587,14 @@ static final int JSON_array_en_main = 1; } -// line 1626 "Parser.java" +// line 1591 "Parser.java" { cs = JSON_array_start; } -// line 717 "Parser.rl" +// line 682 "Parser.rl" -// line 1633 "Parser.java" +// line 1598 "Parser.java" { int _klen; int _trans = 0; @@ -1709,7 +1675,7 @@ case 1: switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 667 "Parser.rl" +// line 632 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -1726,13 +1692,13 @@ case 1: } break; case 1: -// line 682 "Parser.rl" +// line 647 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1737 "Parser.java" +// line 1702 "Parser.java" } } } @@ -1752,7 +1718,7 @@ case 5: break; } } -// line 718 "Parser.rl" +// line 683 "Parser.rl" if (cs >= JSON_array_first_final) { res.update(result, p + 1); @@ -1762,7 +1728,7 @@ case 5: } -// line 1767 "Parser.java" +// line 1732 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -1885,7 +1851,7 @@ static final int JSON_object_error = 0; static final int JSON_object_en_main = 1; -// line 777 "Parser.rl" +// line 742 "Parser.rl" void parseObject(ParserResult res, int p, int pe) { @@ -1910,14 +1876,14 @@ static final int JSON_object_en_main = 1; } -// line 1915 "Parser.java" +// line 1880 "Parser.java" { cs = JSON_object_start; } -// line 801 "Parser.rl" +// line 766 "Parser.rl" -// line 1922 "Parser.java" +// line 1887 "Parser.java" { int _klen; int _trans = 0; @@ -1998,7 +1964,7 @@ case 1: switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 732 "Parser.rl" +// line 697 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -2015,7 +1981,7 @@ case 1: } break; case 1: -// line 747 "Parser.rl" +// line 712 "Parser.rl" { parseString(res, p, pe); if (res.result == null) { @@ -2035,13 +2001,13 @@ case 1: } break; case 2: -// line 765 "Parser.rl" +// line 730 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 2046 "Parser.java" +// line 2011 "Parser.java" } } } @@ -2061,7 +2027,7 @@ case 5: break; } } -// line 802 "Parser.rl" +// line 767 "Parser.rl" if (cs < JSON_object_first_final) { res.update(null, p + 1); @@ -2094,11 +2060,11 @@ case 5: } -// line 2099 "Parser.java" +// line 2064 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { - 0, 1, 0, 1, 1 + 0, 1, 0 }; } @@ -2108,7 +2074,7 @@ private static final byte _JSON_actions[] = init__JSON_actions_0(); private static byte[] init__JSON_key_offsets_0() { return new byte [] { - 0, 0, 7, 9, 10, 12, 13, 15, 16, 18, 19 + 0, 0, 16, 18, 19, 21, 22, 24, 25, 27, 28 }; } @@ -2118,9 +2084,9 @@ private static final byte _JSON_key_offsets[] = init__JSON_key_offsets_0(); private static char[] init__JSON_trans_keys_0() { return new char [] { - 13, 32, 47, 91, 123, 9, 10, 42, 47, 42, 42, 47, - 10, 42, 47, 42, 42, 47, 10, 13, 32, 47, 9, 10, - 0 + 13, 32, 34, 45, 47, 73, 78, 91, 102, 110, 116, 123, + 9, 10, 48, 57, 42, 47, 42, 42, 47, 10, 42, 47, + 42, 42, 47, 10, 13, 32, 47, 9, 10, 0 }; } @@ -2130,7 +2096,7 @@ private static final char _JSON_trans_keys[] = init__JSON_trans_keys_0(); private static byte[] init__JSON_single_lengths_0() { return new byte [] { - 0, 5, 2, 1, 2, 1, 2, 1, 2, 1, 3 + 0, 12, 2, 1, 2, 1, 2, 1, 2, 1, 3 }; } @@ -2140,7 +2106,7 @@ private static final byte _JSON_single_lengths[] = init__JSON_single_lengths_0() private static byte[] init__JSON_range_lengths_0() { return new byte [] { - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; } @@ -2150,7 +2116,7 @@ private static final byte _JSON_range_lengths[] = init__JSON_range_lengths_0(); private static byte[] init__JSON_index_offsets_0() { return new byte [] { - 0, 0, 7, 10, 12, 15, 17, 20, 22, 25, 27 + 0, 0, 15, 18, 20, 23, 25, 28, 30, 33, 35 }; } @@ -2160,9 +2126,10 @@ private static final byte _JSON_index_offsets[] = init__JSON_index_offsets_0(); private static byte[] init__JSON_indicies_0() { return new byte [] { - 0, 0, 2, 3, 4, 0, 1, 5, 6, 1, 7, 5, - 7, 0, 5, 0, 6, 8, 9, 1, 10, 8, 10, 11, - 8, 11, 9, 11, 11, 12, 11, 1, 0 + 0, 0, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 1, 4, 5, 1, 6, 4, 6, 7, 4, 7, + 5, 8, 9, 1, 10, 8, 10, 0, 8, 0, 9, 7, + 7, 11, 7, 1, 0 }; } @@ -2172,8 +2139,7 @@ private static final byte _JSON_indicies[] = init__JSON_indicies_0(); private static byte[] init__JSON_trans_targs_0() { return new byte [] { - 1, 0, 2, 10, 10, 3, 5, 4, 7, 9, 8, 10, - 6 + 1, 0, 10, 6, 3, 5, 4, 10, 7, 9, 8, 2 }; } @@ -2183,8 +2149,7 @@ private static final byte _JSON_trans_targs[] = init__JSON_trans_targs_0(); private static byte[] init__JSON_trans_actions_0() { return new byte [] { - 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; } @@ -2198,26 +2163,26 @@ static final int JSON_error = 0; static final int JSON_en_main = 1; -// line 867 "Parser.rl" +// line 818 "Parser.rl" - public IRubyObject parseStrict() { + public IRubyObject parseImplemetation() { int cs = EVIL; int p, pe; IRubyObject result = null; ParserResult res = new ParserResult(); -// line 2213 "Parser.java" +// line 2177 "Parser.java" { cs = JSON_start; } -// line 876 "Parser.rl" +// line 827 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2222 "Parser.java" +// line 2186 "Parser.java" { int _klen; int _trans = 0; @@ -2298,267 +2263,7 @@ case 1: switch ( _JSON_actions[_acts++] ) { case 0: -// line 839 "Parser.rl" - { - currentNesting = 1; - parseObject(res, p, pe); - if (res.result == null) { - p--; - { p += 1; _goto_targ = 5; if (true) continue _goto;} - } else { - result = res.result; - {p = (( res.p))-1;} - } - } - break; - case 1: -// line 851 "Parser.rl" - { - currentNesting = 1; - parseArray(res, p, pe); - if (res.result == null) { - p--; - { p += 1; _goto_targ = 5; if (true) continue _goto;} - } else { - result = res.result; - {p = (( res.p))-1;} - } - } - break; -// line 2330 "Parser.java" - } - } - } - -case 2: - if ( cs == 0 ) { - _goto_targ = 5; - continue _goto; - } - if ( ++p != pe ) { - _goto_targ = 1; - continue _goto; - } -case 4: -case 5: - } - break; } - } - -// line 879 "Parser.rl" - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - throw unexpectedToken(p, pe); - } - } - - -// line 2360 "Parser.java" -private static byte[] init__JSON_quirks_mode_actions_0() -{ - return new byte [] { - 0, 1, 0 - }; -} - -private static final byte _JSON_quirks_mode_actions[] = init__JSON_quirks_mode_actions_0(); - - -private static byte[] init__JSON_quirks_mode_key_offsets_0() -{ - return new byte [] { - 0, 0, 16, 18, 19, 21, 22, 24, 25, 27, 28 - }; -} - -private static final byte _JSON_quirks_mode_key_offsets[] = init__JSON_quirks_mode_key_offsets_0(); - - -private static char[] init__JSON_quirks_mode_trans_keys_0() -{ - return new char [] { - 13, 32, 34, 45, 47, 73, 78, 91, 102, 110, 116, 123, - 9, 10, 48, 57, 42, 47, 42, 42, 47, 10, 42, 47, - 42, 42, 47, 10, 13, 32, 47, 9, 10, 0 - }; -} - -private static final char _JSON_quirks_mode_trans_keys[] = init__JSON_quirks_mode_trans_keys_0(); - - -private static byte[] init__JSON_quirks_mode_single_lengths_0() -{ - return new byte [] { - 0, 12, 2, 1, 2, 1, 2, 1, 2, 1, 3 - }; -} - -private static final byte _JSON_quirks_mode_single_lengths[] = init__JSON_quirks_mode_single_lengths_0(); - - -private static byte[] init__JSON_quirks_mode_range_lengths_0() -{ - return new byte [] { - 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; -} - -private static final byte _JSON_quirks_mode_range_lengths[] = init__JSON_quirks_mode_range_lengths_0(); - - -private static byte[] init__JSON_quirks_mode_index_offsets_0() -{ - return new byte [] { - 0, 0, 15, 18, 20, 23, 25, 28, 30, 33, 35 - }; -} - -private static final byte _JSON_quirks_mode_index_offsets[] = init__JSON_quirks_mode_index_offsets_0(); - - -private static byte[] init__JSON_quirks_mode_indicies_0() -{ - return new byte [] { - 0, 0, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, - 0, 2, 1, 4, 5, 1, 6, 4, 6, 7, 4, 7, - 5, 8, 9, 1, 10, 8, 10, 0, 8, 0, 9, 7, - 7, 11, 7, 1, 0 - }; -} - -private static final byte _JSON_quirks_mode_indicies[] = init__JSON_quirks_mode_indicies_0(); - - -private static byte[] init__JSON_quirks_mode_trans_targs_0() -{ - return new byte [] { - 1, 0, 10, 6, 3, 5, 4, 10, 7, 9, 8, 2 - }; -} - -private static final byte _JSON_quirks_mode_trans_targs[] = init__JSON_quirks_mode_trans_targs_0(); - - -private static byte[] init__JSON_quirks_mode_trans_actions_0() -{ - return new byte [] { - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; -} - -private static final byte _JSON_quirks_mode_trans_actions[] = init__JSON_quirks_mode_trans_actions_0(); - - -static final int JSON_quirks_mode_start = 1; -static final int JSON_quirks_mode_first_final = 10; -static final int JSON_quirks_mode_error = 0; - -static final int JSON_quirks_mode_en_main = 1; - - -// line 907 "Parser.rl" - - - public IRubyObject parseQuirksMode() { - int cs = EVIL; - int p, pe; - IRubyObject result = null; - ParserResult res = new ParserResult(); - - -// line 2473 "Parser.java" - { - cs = JSON_quirks_mode_start; - } - -// line 916 "Parser.rl" - p = byteList.begin(); - pe = p + byteList.length(); - -// line 2482 "Parser.java" - { - int _klen; - int _trans = 0; - int _acts; - int _nacts; - int _keys; - int _goto_targ = 0; - - _goto: while (true) { - switch ( _goto_targ ) { - case 0: - if ( p == pe ) { - _goto_targ = 4; - continue _goto; - } - if ( cs == 0 ) { - _goto_targ = 5; - continue _goto; - } -case 1: - _match: do { - _keys = _JSON_quirks_mode_key_offsets[cs]; - _trans = _JSON_quirks_mode_index_offsets[cs]; - _klen = _JSON_quirks_mode_single_lengths[cs]; - if ( _klen > 0 ) { - int _lower = _keys; - int _mid; - int _upper = _keys + _klen - 1; - while (true) { - if ( _upper < _lower ) - break; - - _mid = _lower + ((_upper-_lower) >> 1); - if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] ) - _upper = _mid - 1; - else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid] ) - _lower = _mid + 1; - else { - _trans += (_mid - _keys); - break _match; - } - } - _keys += _klen; - _trans += _klen; - } - - _klen = _JSON_quirks_mode_range_lengths[cs]; - if ( _klen > 0 ) { - int _lower = _keys; - int _mid; - int _upper = _keys + (_klen<<1) - 2; - while (true) { - if ( _upper < _lower ) - break; - - _mid = _lower + (((_upper-_lower) >> 1) & ~1); - if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] ) - _upper = _mid - 2; - else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid+1] ) - _lower = _mid + 2; - else { - _trans += ((_mid - _keys)>>1); - break _match; - } - } - _trans += _klen; - } - } while (false); - - _trans = _JSON_quirks_mode_indicies[_trans]; - cs = _JSON_quirks_mode_trans_targs[_trans]; - - if ( _JSON_quirks_mode_trans_actions[_trans] != 0 ) { - _acts = _JSON_quirks_mode_trans_actions[_trans]; - _nacts = (int) _JSON_quirks_mode_actions[_acts++]; - while ( _nacts-- > 0 ) - { - switch ( _JSON_quirks_mode_actions[_acts++] ) - { - case 0: -// line 893 "Parser.rl" +// line 804 "Parser.rl" { parseValue(res, p, pe); if (res.result == null) { @@ -2570,7 +2275,7 @@ case 1: } } break; -// line 2575 "Parser.java" +// line 2279 "Parser.java" } } } @@ -2590,9 +2295,9 @@ case 5: break; } } -// line 919 "Parser.rl" +// line 830 "Parser.rl" - if (cs >= JSON_quirks_mode_first_final && p == pe) { + if (cs >= JSON_first_final && p == pe) { return result; } else { throw unexpectedToken(p, pe); @@ -2600,12 +2305,7 @@ case 5: } public IRubyObject parse() { - if (parser.quirksMode) { - return parseQuirksMode(); - } else { - return parseStrict(); - } - + return parseImplemetation(); } /** diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index d43c74f..28247ea 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -50,10 +50,9 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; - private boolean quirksMode; private RubyClass objectClass; private RubyClass arrayClass; - private RubyHash match_string; + private RubyHash matchString; private static final int DEFAULT_MAX_NESTING = 100; @@ -121,10 +120,6 @@ public class Parser extends RubyObject { * <dd>If set to <code>true</code>, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. * - * <dt><code>:quirks_mode?</code> - * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false - * otherwise. - * * <dt><code>:create_additions</code> * <dd>If set to <code>false</code>, the Parser doesn't create additions * even if a matching class and <code>create_id</code> was found. This option @@ -136,9 +131,6 @@ public class Parser extends RubyObject { * <dt><code>:array_class</code> * <dd>Defaults to Array. * - * <dt><code>:quirks_mode</code> - * <dd>Enables quirks_mode for parser, that is for example parsing single - * JSON values instead of documents is possible. * </dl> */ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) @@ -161,15 +153,20 @@ public class Parser extends RubyObject { this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); - this.quirksMode = opts.getBool("quirks_mode", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", false); this.objectClass = opts.getClass("object_class", runtime.getHash()); this.arrayClass = opts.getClass("array_class", runtime.getArray()); - this.match_string = opts.getHash("match_string"); + this.matchString = opts.getHash("match_string"); + if(symbolizeNames && createAdditions) { + throw runtime.newArgumentError( + "options :symbolize_names and :create_additions cannot be " + + " used in conjunction" + ); + } this.vSource = args[0].convertToString(); - if (!quirksMode) this.vSource = convertEncoding(context, vSource); + this.vSource = convertEncoding(context, vSource); return this; } @@ -180,33 +177,13 @@ public class Parser extends RubyObject { * Returns the source string if no conversion is needed. */ private RubyString convertEncoding(ThreadContext context, RubyString source) { - ByteList bl = source.getByteList(); - int len = bl.length(); - if (len < 2) { - throw Utils.newException(context, Utils.M_PARSER_ERROR, - "A JSON text must at least contain two octets!"); - } - - if (info.encodingsSupported()) { - RubyEncoding encoding = (RubyEncoding)source.encoding(context); - if (encoding != info.ascii8bit.get()) { - return (RubyString)source.encode(context, info.utf8.get()); - } - - String sniffedEncoding = sniffByteList(bl); - if (sniffedEncoding == null) return source; // assume UTF-8 - return reinterpretEncoding(context, source, sniffedEncoding); - } - - String sniffedEncoding = sniffByteList(bl); - if (sniffedEncoding == null) return source; // assume UTF-8 - Ruby runtime = context.getRuntime(); - return (RubyString)info.jsonModule.get(). - callMethod(context, "iconv", - new IRubyObject[] { - runtime.newString("utf-8"), - runtime.newString(sniffedEncoding), - source}); + RubyEncoding encoding = (RubyEncoding)source.encoding(context); + if (encoding == info.ascii8bit.get()) { + source.force_encoding(context, info.utf8.get()); + } else { + source = (RubyString) source.encode(context, info.utf8.get()); + } + return source; } /** @@ -259,17 +236,6 @@ public class Parser extends RubyObject { return checkAndGetSource().dup(); } - /** - * <code>Parser#quirks_mode?()</code> - * - * <p>If set to <code>true</code>, if the parse is in quirks_mode, false - * otherwise. - */ - @JRubyMethod(name = "quirks_mode?") - public IRubyObject quirks_mode_p(ThreadContext context) { - return context.getRuntime().newBoolean(quirksMode); - } - public RubyString checkAndGetSource() { if (vSource != null) { return vSource; @@ -393,7 +359,7 @@ public class Parser extends RubyObject { } } action parse_number { - if (pe > fpc + 9 - (parser.quirksMode ? 1 : 0) && + if (pe > fpc + 8 && absSubSequence(fpc, fpc + 9).equals(JSON_MINUS_INFINITY)) { if (parser.allowNaN) { @@ -623,11 +589,11 @@ public class Parser extends RubyObject { %% write exec; if (parser.createAdditions) { - RubyHash match_string = parser.match_string; - if (match_string != null) { + RubyHash matchString = parser.matchString; + if (matchString != null) { final IRubyObject[] memoArray = { result, null }; try { - match_string.visitAll(new RubyHash.Visitor() { + matchString.visitAll(new RubyHash.Visitor() { @Override public void visit(IRubyObject pattern, IRubyObject klass) { if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) { @@ -648,7 +614,7 @@ public class Parser extends RubyObject { } if (cs >= JSON_string_first_final && result != null) { - if (info.encodingsSupported() && result instanceof RubyString) { + if (result instanceof RubyString) { ((RubyString)result).force_encoding(context, info.utf8.get()); } res.update(result, p + 1); @@ -835,60 +801,6 @@ public class Parser extends RubyObject { write data; - action parse_object { - currentNesting = 1; - parseObject(res, fpc, pe); - if (res.result == null) { - fhold; - fbreak; - } else { - result = res.result; - fexec res.p; - } - } - - action parse_array { - currentNesting = 1; - parseArray(res, fpc, pe); - if (res.result == null) { - fhold; - fbreak; - } else { - result = res.result; - fexec res.p; - } - } - - main := ignore* - ( begin_object >parse_object - | begin_array >parse_array ) - ignore*; - }%% - - public IRubyObject parseStrict() { - int cs = EVIL; - int p, pe; - IRubyObject result = null; - ParserResult res = new ParserResult(); - - %% write init; - p = byteList.begin(); - pe = p + byteList.length(); - %% write exec; - - if (cs >= JSON_first_final && p == pe) { - return result; - } else { - throw unexpectedToken(p, pe); - } - } - - %%{ - machine JSON_quirks_mode; - include JSON_common; - - write data; - action parse_value { parseValue(res, fpc, pe); if (res.result == null) { @@ -905,7 +817,7 @@ public class Parser extends RubyObject { ignore*; }%% - public IRubyObject parseQuirksMode() { + public IRubyObject parseImplemetation() { int cs = EVIL; int p, pe; IRubyObject result = null; @@ -916,7 +828,7 @@ public class Parser extends RubyObject { pe = p + byteList.length(); %% write exec; - if (cs >= JSON_quirks_mode_first_final && p == pe) { + if (cs >= JSON_first_final && p == pe) { return result; } else { throw unexpectedToken(p, pe); @@ -924,12 +836,7 @@ public class Parser extends RubyObject { } public IRubyObject parse() { - if (parser.quirksMode) { - return parseQuirksMode(); - } else { - return parseStrict(); - } - + return parseImplemetation(); } /** diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java index ceaca5b..2323bd9 100644 --- a/java/src/json/ext/RuntimeInfo.java +++ b/java/src/json/ext/RuntimeInfo.java @@ -90,10 +90,6 @@ final class RuntimeInfo { } } - public boolean encodingsSupported() { - return utf8 != null && utf8.get() != null; - } - public RubyEncoding getEncoding(ThreadContext context, String name) { synchronized (encodings) { WeakReference<RubyEncoding> encoding = encodings.get(name); diff --git a/json.gemspec b/json.gemspec Binary files differindex a9493ff..ca023f7 100644 --- a/json.gemspec +++ b/json.gemspec diff --git a/json_pure.gemspec b/json_pure.gemspec index 13c8e8b..c2fb15f 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -8,11 +8,11 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Florian Frank"] - s.date = "2016-02-25" + s.date = "2016-06-07" s.description = "This is a JSON implementation in pure Ruby." s.email = "flori@ping.de" s.extra_rdoc_files = ["README.md"] - s.files = ["./tests/test_helper.rb", ".gitignore", ".travis.yml", "CHANGES", "Gemfile", "README-json-jruby.markdown", "README.md", "Rakefile", "TODO", "VERSION", "data/example.json", "data/index.html", "data/prototype.js", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/obsolete_fail1.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/json_addition_test.rb", "tests/json_common_interface_test.rb", "tests/json_encoding_test.rb", "tests/json_ext_parser_test.rb", "tests/json_fixtures_test.rb", "tests/json_generator_test.rb", "tests/json_generic_object_test.rb", "tests/json_parser_test.rb", "tests/json_string_matching_test.rb", "tests/test_helper.rb", "tools/fuzz.rb", "tools/server.rb"] + s.files = ["./tests/test_helper.rb", ".gitignore", ".travis.yml", "CHANGES", "Gemfile", "README-json-jruby.markdown", "README.md", "Rakefile", "VERSION", "data/example.json", "data/index.html", "data/prototype.js", "diagrams/.keep", "ext/json/ext/fbuffer/fbuffer.h", "ext/json/ext/generator/depend", "ext/json/ext/generator/extconf.rb", "ext/json/ext/generator/generator.c", "ext/json/ext/generator/generator.h", "ext/json/ext/parser/depend", "ext/json/ext/parser/extconf.rb", "ext/json/ext/parser/parser.c", "ext/json/ext/parser/parser.h", "ext/json/ext/parser/parser.rl", "ext/json/extconf.rb", "install.rb", "java/src/json/ext/ByteListTranscoder.java", "java/src/json/ext/Generator.java", "java/src/json/ext/GeneratorMethods.java", "java/src/json/ext/GeneratorService.java", "java/src/json/ext/GeneratorState.java", "java/src/json/ext/OptionsReader.java", "java/src/json/ext/Parser.java", "java/src/json/ext/Parser.rl", "java/src/json/ext/ParserService.java", "java/src/json/ext/RuntimeInfo.java", "java/src/json/ext/StringDecoder.java", "java/src/json/ext/StringEncoder.java", "java/src/json/ext/Utils.java", "json-java.gemspec", "json.gemspec", "json_pure.gemspec", "lib/json.rb", "lib/json/add/bigdecimal.rb", "lib/json/add/complex.rb", "lib/json/add/core.rb", "lib/json/add/date.rb", "lib/json/add/date_time.rb", "lib/json/add/exception.rb", "lib/json/add/ostruct.rb", "lib/json/add/range.rb", "lib/json/add/rational.rb", "lib/json/add/regexp.rb", "lib/json/add/struct.rb", "lib/json/add/symbol.rb", "lib/json/add/time.rb", "lib/json/common.rb", "lib/json/ext.rb", "lib/json/ext/.keep", "lib/json/generic_object.rb", "lib/json/pure.rb", "lib/json/pure/generator.rb", "lib/json/pure/parser.rb", "lib/json/version.rb", "references/rfc7159.txt", "tests/fixtures/fail10.json", "tests/fixtures/fail11.json", "tests/fixtures/fail12.json", "tests/fixtures/fail13.json", "tests/fixtures/fail14.json", "tests/fixtures/fail18.json", "tests/fixtures/fail19.json", "tests/fixtures/fail2.json", "tests/fixtures/fail20.json", "tests/fixtures/fail21.json", "tests/fixtures/fail22.json", "tests/fixtures/fail23.json", "tests/fixtures/fail24.json", "tests/fixtures/fail25.json", "tests/fixtures/fail27.json", "tests/fixtures/fail28.json", "tests/fixtures/fail3.json", "tests/fixtures/fail4.json", "tests/fixtures/fail5.json", "tests/fixtures/fail6.json", "tests/fixtures/fail7.json", "tests/fixtures/fail8.json", "tests/fixtures/fail9.json", "tests/fixtures/obsolete_fail1.json", "tests/fixtures/pass1.json", "tests/fixtures/pass15.json", "tests/fixtures/pass16.json", "tests/fixtures/pass17.json", "tests/fixtures/pass2.json", "tests/fixtures/pass26.json", "tests/fixtures/pass3.json", "tests/json_addition_test.rb", "tests/json_common_interface_test.rb", "tests/json_encoding_test.rb", "tests/json_ext_parser_test.rb", "tests/json_fixtures_test.rb", "tests/json_generator_test.rb", "tests/json_generic_object_test.rb", "tests/json_parser_test.rb", "tests/json_string_matching_test.rb", "tests/test_helper.rb", "tools/fuzz.rb", "tools/server.rb"] s.homepage = "http://flori.github.com/json" s.licenses = ["Ruby"] s.rdoc_options = ["--title", "JSON implemention for ruby", "--main", "README.md"] diff --git a/lib/json/common.rb b/lib/json/common.rb index 57001b5..b53041b 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -25,7 +25,7 @@ module JSON # Set the JSON parser class _parser_ to be used by JSON. def parser=(parser) # :nodoc: @parser = parser - remove_const :Parser if JSON.const_defined_in?(self, :Parser) + remove_const :Parser if const_defined?(:Parser, true) const_set :Parser, parser end @@ -36,8 +36,8 @@ module JSON def deep_const_get(path) # :nodoc: path.to_s.split(/::/).inject(Object) do |p, c| case - when c.empty? then p - when JSON.const_defined_in?(p, c) then p.const_get(c) + when c.empty? then p + when p.const_defined?(c, true) then p.const_get(c) else begin p.const_missing(c) @@ -142,7 +142,7 @@ module JSON # structures. Disable depth checking with :max_nesting => false. It # defaults to 100. # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # defiance of RFC 7159 to be parsed by the Parser. This option defaults # to false. # * *symbolize_names*: If set to true, returns symbols for the names # (keys) in a JSON object. Otherwise strings are returned. Strings are @@ -166,7 +166,7 @@ module JSON # parse! methods defaults to not doing max depth checking: This can be # dangerous if someone wants to fill up your stack. # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in - # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # defiance of RFC 7159 to be parsed by the Parser. This option defaults # to true. # * *create_additions*: If set to false, the Parser doesn't create # additions even if a matching class and create_id was found. This option @@ -175,7 +175,7 @@ module JSON opts = { :max_nesting => false, :allow_nan => true - }.update(opts) + }.merge(opts) Parser.new(source, opts).parse end @@ -296,13 +296,13 @@ module JSON # The global default options for the JSON.load method: # :max_nesting: false # :allow_nan: true - # :quirks_mode: true + # :allow_blank: true attr_accessor :load_default_options end self.load_default_options = { :max_nesting => false, :allow_nan => true, - :quirks_mode => true, + :allow_blank => true, :create_additions => true, } @@ -329,7 +329,7 @@ module JSON elsif source.respond_to?(:read) source = source.read end - if opts[:quirks_mode] && (source.nil? || source.empty?) + if opts[:allow_blank] && (source.nil? || source.empty?) source = 'null' end result = parse(source, opts) @@ -358,13 +358,12 @@ module JSON # The global default options for the JSON.dump method: # :max_nesting: false # :allow_nan: true - # :quirks_mode: true + # :allow_blank: true attr_accessor :dump_default_options end self.dump_default_options = { :max_nesting => false, :allow_nan => true, - :quirks_mode => true, } # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns @@ -403,37 +402,9 @@ module JSON raise ArgumentError, "exceed depth limit" end - # Swap consecutive bytes of _string_ in place. - def self.swap!(string) # :nodoc: - 0.upto(string.size / 2) do |i| - break unless string[2 * i + 1] - string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i] - end - string - end - - # Shortcut for iconv. - if ::String.method_defined?(:encode) - # Encodes string using Ruby's _String.encode_ - def self.iconv(to, from, string) - string.encode(to, from) - end - else - require 'iconv' - # Encodes string using _iconv_ library - def self.iconv(to, from, string) - Iconv.conv(to, from, string) - end - end - - if ::Object.method(:const_defined?).arity == 1 - def self.const_defined_in?(modul, constant) - modul.const_defined?(constant) - end - else - def self.const_defined_in?(modul, constant) - modul.const_defined?(constant, false) - end + # Encodes string using Ruby's _String.encode_ + def self.iconv(to, from, string) + string.encode(to, from) end end diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 44c2b65..7264a85 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -1,10 +1,3 @@ -#frozen_string_literal: false -if ENV['SIMPLECOV_COVERAGE'].to_i == 1 - require 'simplecov' - SimpleCov.start do - add_filter "/tests/" - end -end require 'json/common' module JSON diff --git a/lib/json/pure.rb b/lib/json/pure.rb index 6bee334..53178b3 100644 --- a/lib/json/pure.rb +++ b/lib/json/pure.rb @@ -1,18 +1,11 @@ -#frozen_string_literal: false -if ENV['SIMPLECOV_COVERAGE'].to_i == 1 - require 'simplecov' - SimpleCov.start do - add_filter "/tests/" - end -end require 'json/common' -require 'json/pure/parser' -require 'json/pure/generator' module JSON # This module holds all the modules/classes that implement JSON's # functionality in pure ruby. module Pure + require 'json/pure/parser' + require 'json/pure/generator' $DEBUG and warn "Using Pure library for JSON." JSON.parser = Parser JSON.generator = Generator diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index fdb0e2a..ccb6fe4 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -39,85 +39,45 @@ module JSON # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with # UTF16 big endian characters as \u????, and return it. - if defined?(::Encoding) - def utf8_to_json(string) # :nodoc: - string = string.dup - string.force_encoding(::Encoding::ASCII_8BIT) - string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] } - string.force_encoding(::Encoding::UTF_8) - string - end - - def utf8_to_json_ascii(string) # :nodoc: - string = string.dup - string.force_encoding(::Encoding::ASCII_8BIT) - string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] } - string.gsub!(/( - (?: - [\xc2-\xdf][\x80-\xbf] | - [\xe0-\xef][\x80-\xbf]{2} | - [\xf0-\xf4][\x80-\xbf]{3} - )+ | - [\x80-\xc1\xf5-\xff] # invalid - )/nx) { |c| - c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'" - s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0] - s.force_encoding(::Encoding::ASCII_8BIT) - s.gsub!(/.{4}/n, '\\\\u\&') - s.force_encoding(::Encoding::UTF_8) - } - string.force_encoding(::Encoding::UTF_8) - string - rescue => e - raise GeneratorError.wrap(e) - end - - def valid_utf8?(string) - encoding = string.encoding - (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) && - string.valid_encoding? - end - module_function :valid_utf8? - else - def utf8_to_json(string) # :nodoc: - string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] } - end + def utf8_to_json(string) # :nodoc: + string = string.dup + string.force_encoding(::Encoding::ASCII_8BIT) + string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] } + string.force_encoding(::Encoding::UTF_8) + string + end - def utf8_to_json_ascii(string) # :nodoc: - string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] } - string.gsub!(/( - (?: - [\xc2-\xdf][\x80-\xbf] | - [\xe0-\xef][\x80-\xbf]{2} | - [\xf0-\xf4][\x80-\xbf]{3} - )+ | - [\x80-\xc1\xf5-\xff] # invalid - )/nx) { |c| - c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'" - s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0] - s.gsub!(/.{4}/n, '\\\\u\&') - } - string - rescue => e - raise GeneratorError.wrap(e) - end + def utf8_to_json_ascii(string) # :nodoc: + string = string.dup + string.force_encoding(::Encoding::ASCII_8BIT) + string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] } + string.gsub!(/( + (?: + [\xc2-\xdf][\x80-\xbf] | + [\xe0-\xef][\x80-\xbf]{2} | + [\xf0-\xf4][\x80-\xbf]{3} + )+ | + [\x80-\xc1\xf5-\xff] # invalid + )/nx) { |c| + c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'" + s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0] + s.force_encoding(::Encoding::ASCII_8BIT) + s.gsub!(/.{4}/n, '\\\\u\&') + s.force_encoding(::Encoding::UTF_8) + } + string.force_encoding(::Encoding::UTF_8) + string + rescue => e + raise GeneratorError.wrap(e) + end - def valid_utf8?(string) - string =~ - /\A( [\x09\x0a\x0d\x20-\x7e] # ASCII - | [\xc2-\xdf][\x80-\xbf] # non-overlong 2-byte - | \xe0[\xa0-\xbf][\x80-\xbf] # excluding overlongs - | [\xe1-\xec\xee\xef][\x80-\xbf]{2} # straight 3-byte - | \xed[\x80-\x9f][\x80-\xbf] # excluding surrogates - | \xf0[\x90-\xbf][\x80-\xbf]{2} # planes 1-3 - | [\xf1-\xf3][\x80-\xbf]{3} # planes 4-15 - | \xf4[\x80-\x8f][\x80-\xbf]{2} # plane 16 - )*\z/nx - end + def valid_utf8?(string) + encoding = string.encoding + (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) && + string.valid_encoding? end module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8? - module Pure module Generator # This class is used to create State instances, that are use to hold data @@ -155,8 +115,6 @@ module JSON # * *allow_nan*: true if NaN, Infinity, and -Infinity should be # generated, otherwise an exception is thrown, if these values are # encountered. This options defaults to false. - # * *quirks_mode*: Enables quirks_mode for parser, that is for example - # generating single JSON values instead of documents is possible. def initialize(opts = {}) @indent = '' @space = '' @@ -165,7 +123,6 @@ module JSON @array_nl = '' @allow_nan = false @ascii_only = false - @quirks_mode = false @buffer_initial_length = 1024 configure opts end @@ -191,10 +148,6 @@ module JSON # the generated JSON, max_nesting = 0 if no maximum is checked. attr_accessor :max_nesting - # If this attribute is set to true, quirks mode is enabled, otherwise - # it's disabled. - attr_accessor :quirks_mode - # :stopdoc: attr_reader :buffer_initial_length @@ -234,11 +187,6 @@ module JSON @ascii_only end - # Returns true, if quirks mode is enabled. Otherwise returns false. - def quirks_mode? - @quirks_mode - end - # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -260,7 +208,6 @@ module JSON @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) @depth = opts[:depth] || 0 - @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode) @buffer_initial_length ||= opts[:buffer_initial_length] if !opts.key?(:max_nesting) # defaults to 100 @@ -440,34 +387,20 @@ module JSON end module String - if defined?(::Encoding) - # This string should be encoded with UTF-8 A call to this method - # returns a JSON string encoded with UTF16 big endian characters as - # \u????. - def to_json(state = nil, *args) - state = State.from_state(state) - if encoding == ::Encoding::UTF_8 - string = self - else - string = encode(::Encoding::UTF_8) - end - if state.ascii_only? - '"' << JSON.utf8_to_json_ascii(string) << '"' - else - '"' << JSON.utf8_to_json(string) << '"' - end + # This string should be encoded with UTF-8 A call to this method + # returns a JSON string encoded with UTF16 big endian characters as + # \u????. + def to_json(state = nil, *args) + state = State.from_state(state) + if encoding == ::Encoding::UTF_8 + string = self + else + string = encode(::Encoding::UTF_8) end - else - # This string should be encoded with UTF-8 A call to this method - # returns a JSON string encoded with UTF16 big endian characters as - # \u????. - def to_json(state = nil, *args) - state = State.from_state(state) - if state.ascii_only? - '"' << JSON.utf8_to_json_ascii(self) << '"' - else - '"' << JSON.utf8_to_json(self) << '"' - end + if state.ascii_only? + '"' << JSON.utf8_to_json_ascii(string) << '"' + else + '"' << JSON.utf8_to_json(string) << '"' end end diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index f80e3b2..b907236 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -59,7 +59,7 @@ module JSON # structures. Disable depth checking with :max_nesting => false|nil|0, # it defaults to 100. # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in - # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # defiance of RFC 7159 to be parsed by the Parser. This option defaults # to false. # * *symbolize_names*: If set to true, returns symbols for the names # (keys) in a JSON object. Otherwise strings are returned, which is @@ -70,13 +70,9 @@ module JSON # option defaults to false. # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array - # * *quirks_mode*: Enables quirks_mode for parser, that is for example - # parsing single JSON values instead of documents is possible. def initialize(source, opts = {}) opts ||= {} - unless @quirks_mode = opts[:quirks_mode] - source = convert_encoding source - end + source = convert_encoding source super source if !opts.key?(:max_nesting) # defaults to 100 @max_nesting = 100 @@ -103,10 +99,6 @@ module JSON alias source string - def quirks_mode? - !!@quirks_mode - end - def reset super @current_nesting = 0 @@ -139,7 +131,7 @@ module JSON raise TypeError, "#{source.inspect} is not like a string" end - if defined?(::Encoding) + if source.encoding != ::Encoding::ASCII_8BIT source = source.encode(::Encoding::UTF_8) source.force_encoding(::Encoding::ASCII_8BIT) end diff --git a/lib/json/version.rb b/lib/json/version.rb index b4d1b3b..520a8ad 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,4 +1,4 @@ -#frozen_string_literal: false +# frozen_string_literal: false module JSON # JSON version VERSION = '2.0.0' diff --git a/references/rfc7159.txt b/references/rfc7159.txt new file mode 100644 index 0000000..64fcada --- /dev/null +++ b/references/rfc7159.txt @@ -0,0 +1,899 @@ + + + + + + +Internet Engineering Task Force (IETF) T. Bray, Ed. +Request for Comments: 7159 Google, Inc. +Obsoletes: 4627, 7158 March 2014 +Category: Standards Track +ISSN: 2070-1721 + + + The JavaScript Object Notation (JSON) Data Interchange Format + +Abstract + + JavaScript Object Notation (JSON) is a lightweight, text-based, + language-independent data interchange format. It was derived from + the ECMAScript Programming Language Standard. JSON defines a small + set of formatting rules for the portable representation of structured + data. + + This document removes inconsistencies with other specifications of + JSON, repairs specification errors, and offers experience-based + interoperability guidance. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc7159. + + + + + + + + + + + + + + + + + +Bray Standards Track [Page 1] + +RFC 7159 JSON March 2014 + + +Copyright Notice + + Copyright (c) 2014 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + + + + + + + + + + + + + + + + + + + + + + + + + +Bray Standards Track [Page 2] + +RFC 7159 JSON March 2014 + + +Table of Contents + + 1. Introduction ....................................................3 + 1.1. Conventions Used in This Document ..........................4 + 1.2. Specifications of JSON .....................................4 + 1.3. Introduction to This Revision ..............................4 + 2. JSON Grammar ....................................................4 + 3. Values ..........................................................5 + 4. Objects .........................................................6 + 5. Arrays ..........................................................6 + 6. Numbers .........................................................6 + 7. Strings .........................................................8 + 8. String and Character Issues .....................................9 + 8.1. Character Encoding .........................................9 + 8.2. Unicode Characters .........................................9 + 8.3. String Comparison ..........................................9 + 9. Parsers ........................................................10 + 10. Generators ....................................................10 + 11. IANA Considerations ...........................................10 + 12. Security Considerations .......................................11 + 13. Examples ......................................................12 + 14. Contributors ..................................................13 + 15. References ....................................................13 + 15.1. Normative References .....................................13 + 15.2. Informative References ...................................13 + Appendix A. Changes from RFC 4627 .................................15 + +1. Introduction + + JavaScript Object Notation (JSON) is a text format for the + serialization of structured data. It is derived from the object + literals of JavaScript, as defined in the ECMAScript Programming + Language Standard, Third Edition [ECMA-262]. + + JSON can represent four primitive types (strings, numbers, booleans, + and null) and two structured types (objects and arrays). + + A string is a sequence of zero or more Unicode characters [UNICODE]. + Note that this citation references the latest version of Unicode + rather than a specific release. It is not expected that future + changes in the UNICODE specification will impact the syntax of JSON. + + An object is an unordered collection of zero or more name/value + pairs, where a name is a string and a value is a string, number, + boolean, null, object, or array. + + An array is an ordered sequence of zero or more values. + + + + +Bray Standards Track [Page 3] + +RFC 7159 JSON March 2014 + + + The terms "object" and "array" come from the conventions of + JavaScript. + + JSON's design goals were for it to be minimal, portable, textual, and + a subset of JavaScript. + +1.1. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The grammatical rules in this document are to be interpreted as + described in [RFC5234]. + +1.2. Specifications of JSON + + This document updates [RFC4627], which describes JSON and registers + the media type "application/json". + + A description of JSON in ECMAScript terms appears in Version 5.1 of + the ECMAScript specification [ECMA-262], Section 15.12. JSON is also + described in [ECMA-404]. + + All of the specifications of JSON syntax agree on the syntactic + elements of the language. + +1.3. Introduction to This Revision + + In the years since the publication of RFC 4627, JSON has found very + wide use. This experience has revealed certain patterns, which, + while allowed by its specifications, have caused interoperability + problems. + + Also, a small number of errata have been reported (see RFC Errata IDs + 607 [Err607] and 3607 [Err3607]). + + This document's goal is to apply the errata, remove inconsistencies + with other specifications of JSON, and highlight practices that can + lead to interoperability problems. + +2. JSON Grammar + + A JSON text is a sequence of tokens. The set of tokens includes six + structural characters, strings, numbers, and three literal names. + + A JSON text is a serialized value. Note that certain previous + specifications of JSON constrained a JSON text to be an object or an + + + +Bray Standards Track [Page 4] + +RFC 7159 JSON March 2014 + + + array. Implementations that generate only objects or arrays where a + JSON text is called for will be interoperable in the sense that all + implementations will accept these as conforming JSON texts. + + JSON-text = ws value ws + + These are the six structural characters: + + begin-array = ws %x5B ws ; [ left square bracket + + begin-object = ws %x7B ws ; { left curly bracket + + end-array = ws %x5D ws ; ] right square bracket + + end-object = ws %x7D ws ; } right curly bracket + + name-separator = ws %x3A ws ; : colon + + value-separator = ws %x2C ws ; , comma + + Insignificant whitespace is allowed before or after any of the six + structural characters. + + ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ) ; Carriage return + +3. Values + + A JSON value MUST be an object, array, number, or string, or one of + the following three literal names: + + false null true + + The literal names MUST be lowercase. No other literal names are + allowed. + + value = false / null / true / object / array / number / string + + false = %x66.61.6c.73.65 ; false + + null = %x6e.75.6c.6c ; null + + true = %x74.72.75.65 ; true + + + + + +Bray Standards Track [Page 5] + +RFC 7159 JSON March 2014 + + +4. Objects + + An object structure is represented as a pair of curly brackets + surrounding zero or more name/value pairs (or members). A name is a + string. A single colon comes after each name, separating the name + from the value. A single comma separates a value from a following + name. The names within an object SHOULD be unique. + + object = begin-object [ member *( value-separator member ) ] + end-object + + member = string name-separator value + + An object whose names are all unique is interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. When the names within an object are not + unique, the behavior of software that receives such an object is + unpredictable. Many implementations report the last name/value pair + only. Other implementations report an error or fail to parse the + object, and some implementations report all of the name/value pairs, + including duplicates. + + JSON parsing libraries have been observed to differ as to whether or + not they make the ordering of object members visible to calling + software. Implementations whose behavior does not depend on member + ordering will be interoperable in the sense that they will not be + affected by these differences. + +5. Arrays + + An array structure is represented as square brackets surrounding zero + or more values (or elements). Elements are separated by commas. + + array = begin-array [ value *( value-separator value ) ] end-array + + There is no requirement that the values in an array be of the same + type. + +6. Numbers + + The representation of numbers is similar to that used in most + programming languages. A number is represented in base 10 using + decimal digits. It contains an integer component that may be + prefixed with an optional minus sign, which may be followed by a + fraction part and/or an exponent part. Leading zeros are not + allowed. + + A fraction part is a decimal point followed by one or more digits. + + + +Bray Standards Track [Page 6] + +RFC 7159 JSON March 2014 + + + An exponent part begins with the letter E in upper or lower case, + which may be followed by a plus or minus sign. The E and optional + sign are followed by one or more digits. + + Numeric values that cannot be represented in the grammar below (such + as Infinity and NaN) are not permitted. + + number = [ minus ] int [ frac ] [ exp ] + + decimal-point = %x2E ; . + + digit1-9 = %x31-39 ; 1-9 + + e = %x65 / %x45 ; e E + + exp = e [ minus / plus ] 1*DIGIT + + frac = decimal-point 1*DIGIT + + int = zero / ( digit1-9 *DIGIT ) + + minus = %x2D ; - + + plus = %x2B ; + + + zero = %x30 ; 0 + + This specification allows implementations to set limits on the range + and precision of numbers accepted. Since software that implements + IEEE 754-2008 binary64 (double precision) numbers [IEEE754] is + generally available and widely used, good interoperability can be + achieved by implementations that expect no more precision or range + than these provide, in the sense that implementations will + approximate JSON numbers within the expected precision. A JSON + number such as 1E400 or 3.141592653589793238462643383279 may indicate + potential interoperability problems, since it suggests that the + software that created it expects receiving software to have greater + capabilities for numeric magnitude and precision than is widely + available. + + Note that when such software is used, numbers that are integers and + are in the range [-(2**53)+1, (2**53)-1] are interoperable in the + sense that implementations will agree exactly on their numeric + values. + + + + + + + +Bray Standards Track [Page 7] + +RFC 7159 JSON March 2014 + + +7. Strings + + The representation of strings is similar to conventions used in the C + family of programming languages. A string begins and ends with + quotation marks. All Unicode characters may be placed within the + quotation marks, except for the characters that must be escaped: + quotation mark, reverse solidus, and the control characters (U+0000 + through U+001F). + + Any character may be escaped. If the character is in the Basic + Multilingual Plane (U+0000 through U+FFFF), then it may be + represented as a six-character sequence: a reverse solidus, followed + by the lowercase letter u, followed by four hexadecimal digits that + encode the character's code point. The hexadecimal letters A though + F can be upper or lower case. So, for example, a string containing + only a single reverse solidus character may be represented as + "\u005C". + + Alternatively, there are two-character sequence escape + representations of some popular characters. So, for example, a + string containing only a single reverse solidus character may be + represented more compactly as "\\". + + To escape an extended character that is not in the Basic Multilingual + Plane, the character is represented as a 12-character sequence, + encoding the UTF-16 surrogate pair. So, for example, a string + containing only the G clef character (U+1D11E) may be represented as + "\uD834\uDD1E". + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + + + +Bray Standards Track [Page 8] + +RFC 7159 JSON March 2014 + + +8. String and Character Issues + +8.1. Character Encoding + + JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32. The default + encoding is UTF-8, and JSON texts that are encoded in UTF-8 are + interoperable in the sense that they will be read successfully by the + maximum number of implementations; there are many implementations + that cannot successfully read texts in other encodings (such as + UTF-16 and UTF-32). + + Implementations MUST NOT add a byte order mark to the beginning of a + JSON text. In the interests of interoperability, implementations + that parse JSON texts MAY ignore the presence of a byte order mark + rather than treating it as an error. + +8.2. Unicode Characters + + When all the strings represented in a JSON text are composed entirely + of Unicode characters [UNICODE] (however escaped), then that JSON + text is interoperable in the sense that all software implementations + that parse it will agree on the contents of names and of string + values in objects and arrays. + + However, the ABNF in this specification allows member names and + string values to contain bit sequences that cannot encode Unicode + characters; for example, "\uDEAD" (a single unpaired UTF-16 + surrogate). Instances of this have been observed, for example, when + a library truncates a UTF-16 string without checking whether the + truncation split a surrogate pair. The behavior of software that + receives JSON texts containing such values is unpredictable; for + example, implementations might return different values for the length + of a string value or even suffer fatal runtime exceptions. + +8.3. String Comparison + + Software implementations are typically required to test names of + object members for equality. Implementations that transform the + textual representation into sequences of Unicode code units and then + perform the comparison numerically, code unit by code unit, are + interoperable in the sense that implementations will agree in all + cases on equality or inequality of two strings. For example, + implementations that compare strings with escaped characters + unconverted may incorrectly find that "a\\b" and "a\u005Cb" are not + equal. + + + + + + +Bray Standards Track [Page 9] + +RFC 7159 JSON March 2014 + + +9. Parsers + + A JSON parser transforms a JSON text into another representation. A + JSON parser MUST accept all texts that conform to the JSON grammar. + A JSON parser MAY accept non-JSON forms or extensions. + + An implementation may set limits on the size of texts that it + accepts. An implementation may set limits on the maximum depth of + nesting. An implementation may set limits on the range and precision + of numbers. An implementation may set limits on the length and + character contents of strings. + +10. Generators + + A JSON generator produces JSON text. The resulting text MUST + strictly conform to the JSON grammar. + +11. IANA Considerations + + The MIME media type for JSON text is application/json. + + Type name: application + + Subtype name: json + + Required parameters: n/a + + Optional parameters: n/a + + Encoding considerations: binary + + Security considerations: See [RFC7159], Section 12. + + Interoperability considerations: Described in [RFC7159] + + Published specification: [RFC7159] + + Applications that use this media type: + JSON has been used to exchange data between applications written + in all of these programming languages: ActionScript, C, C#, + Clojure, ColdFusion, Common Lisp, E, Erlang, Go, Java, JavaScript, + Lua, Objective CAML, Perl, PHP, Python, Rebol, Ruby, Scala, and + Scheme. + + + + + + + + +Bray Standards Track [Page 10] + +RFC 7159 JSON March 2014 + + + Additional information: + Magic number(s): n/a + File extension(s): .json + Macintosh file type code(s): TEXT + + Person & email address to contact for further information: + IESG + <iesg@ietf.org> + + Intended usage: COMMON + + Restrictions on usage: none + + Author: + Douglas Crockford + <douglas@crockford.com> + + Change controller: + IESG + <iesg@ietf.org> + + Note: No "charset" parameter is defined for this registration. + Adding one really has no effect on compliant recipients. + +12. Security Considerations + + Generally, there are security issues with scripting languages. JSON + is a subset of JavaScript but excludes assignment and invocation. + + Since JSON's syntax is borrowed from JavaScript, it is possible to + use that language's "eval()" function to parse JSON texts. This + generally constitutes an unacceptable security risk, since the text + could contain executable code along with data declarations. The same + consideration applies to the use of eval()-like functions in any + other programming language in which JSON texts conform to that + language's syntax. + + + + + + + + + + + + + + + +Bray Standards Track [Page 11] + +RFC 7159 JSON March 2014 + + +13. Examples + + This is a JSON object: + + { + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + } + + Its Image member is an object whose Thumbnail member is an object and + whose IDs member is an array of numbers. + + This is a JSON array containing two objects: + + [ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] + + + + + +Bray Standards Track [Page 12] + +RFC 7159 JSON March 2014 + + + Here are three small JSON texts containing only values: + + "Hello world!" + + 42 + + true + +14. Contributors + + RFC 4627 was written by Douglas Crockford. This document was + constructed by making a relatively small number of changes to that + document; thus, the vast majority of the text here is his. + +15. References + +15.1. Normative References + + [IEEE754] IEEE, "IEEE Standard for Floating-Point Arithmetic", IEEE + Standard 754, August 2008, + <http://grouper.ieee.org/groups/754/>. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", STD 68, RFC 5234, January 2008. + + [UNICODE] The Unicode Consortium, "The Unicode Standard", + <http://www.unicode.org/versions/latest/>. + +15.2. Informative References + + [ECMA-262] Ecma International, "ECMAScript Language Specification + Edition 5.1", Standard ECMA-262, June 2011, + <http://www.ecma-international.org/publications/standards/ + Ecma-262.htm>. + + [ECMA-404] Ecma International, "The JSON Data Interchange Format", + Standard ECMA-404, October 2013, + <http://www.ecma-international.org/publications/standards/ + Ecma-404.htm>. + + [Err3607] RFC Errata, Errata ID 3607, RFC 3607, + <http://www.rfc-editor.org>. + + + + + + +Bray Standards Track [Page 13] + +RFC 7159 JSON March 2014 + + + [Err607] RFC Errata, Errata ID 607, RFC 607, + <http://www.rfc-editor.org>. + + [RFC4627] Crockford, D., "The application/json Media Type for + JavaScript Object Notation (JSON)", RFC 4627, July 2006. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bray Standards Track [Page 14] + +RFC 7159 JSON March 2014 + + +Appendix A. Changes from RFC 4627 + + This section lists changes between this document and the text in RFC + 4627. + + o Changed the title and abstract of the document. + + o Changed the reference to [UNICODE] to be not version specific. + + o Added a "Specifications of JSON" section. + + o Added an "Introduction to This Revision" section. + + o Changed the definition of "JSON text" so that it can be any JSON + value, removing the constraint that it be an object or array. + + o Added language about duplicate object member names, member + ordering, and interoperability. + + o Clarified the absence of a requirement that values in an array be + of the same JSON type. + + o Applied erratum #607 from RFC 4627 to correctly align the artwork + for the definition of "object". + + o Changed "as sequences of digits" to "in the grammar below" in the + "Numbers" section, and made base-10-ness explicit. + + o Added language about number interoperability as a function of + IEEE754, and added an IEEE754 reference. + + o Added language about interoperability and Unicode characters and + about string comparisons. To do this, turned the old "Encoding" + section into a "String and Character Issues" section, with three + subsections: "Character Encoding", "Unicode Characters", and + "String Comparison". + + o Changed guidance in the "Parsers" section to point out that + implementations may set limits on the range "and precision" of + numbers. + + o Updated and tidied the "IANA Considerations" section. + + o Made a real "Security Considerations" section and lifted the text + out of the previous "IANA Considerations" section. + + + + + + +Bray Standards Track [Page 15] + +RFC 7159 JSON March 2014 + + + o Applied erratum #3607 from RFC 4627 by removing the security + consideration that begins "A JSON text can be safely passed" and + the JavaScript code that went with that consideration. + + o Added a note to the "Security Considerations" section pointing out + the risks of using the "eval()" function in JavaScript or any + other language in which JSON texts conform to that language's + syntax. + + o Added a note to the "IANA Considerations" clarifying the absence + of a "charset" parameter for the application/json media type. + + o Changed "100" to 100 and added a boolean field, both in the first + example. + + o Added examples of JSON texts with simple values, neither objects + nor arrays. + + o Added a "Contributors" section crediting Douglas Crockford. + + o Added a reference to RFC 4627. + + o Moved the ECMAScript reference from Normative to Informative and + updated it to reference ECMAScript 5.1, and added a reference to + ECMA 404. + +Author's Address + + Tim Bray (editor) + Google, Inc. + + EMail: tbray@textuality.com + + + + + + + + + + + + + + + + + + + +Bray Standards Track [Page 16] + diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index b09f857..b2051d4 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -18,37 +18,57 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase 'i' => 0.001 } @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\ - '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}' + '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}' end def test_index + assert_equal @json, JSON[@hash] + assert_equal @hash, JSON[@json] end def test_parser + assert_match /::Parser\z/, JSON.parser.name end def test_generator + assert_match /::Generator\z/, JSON.generator.name end def test_state + assert_match /::Generator::State\z/, JSON.state.name end def test_create_id + assert_equal 'json_class', JSON.create_id + JSON.create_id = 'foo_bar' + assert_equal 'foo_bar', JSON.create_id + ensure + JSON.create_id = 'json_class' + end + + def test_deep_const_get + assert_raises(ArgumentError) { JSON.deep_const_get('Nix::Da') } + assert_equal File::SEPARATOR, JSON.deep_const_get('File::SEPARATOR') end def test_parse + assert_equal [ 1, 2, 3, ], JSON.parse('[ 1, 2, 3 ]') end def test_parse_bang + assert_equal [ 1, NaN, 3, ], JSON.parse!('[ 1, NaN, 3 ]') end def test_generate + assert_equal '[1,2,3]', JSON.generate([ 1, 2, 3 ]) end def test_fast_generate + assert_equal '[1,2,3]', JSON.generate([ 1, 2, 3 ]) end def test_pretty_generate + assert_equal "[\n 1,\n 2,\n 3\n]", JSON.pretty_generate([ 1, 2, 3 ]) end def test_load @@ -71,6 +91,12 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase assert JSON.load(json, nil, :allow_nan => true)['foo'].nan? end + def test_load_null + assert_equal nil, JSON.load(nil, nil, :allow_blank => true) + assert_raises(TypeError) { JSON.load(nil, nil, :allow_blank => false) } + assert_raises(JSON::ParserError) { JSON.load('', nil, :allow_blank => false) } + end + def test_dump too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]' assert_equal too_deep, dump(eval(too_deep)) @@ -93,7 +119,8 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase assert_equal max_nesting, JSON.dump_default_options[:max_nesting] end - def test_JSON + assert_equal @json, JSON(@hash) + assert_equal @hash, JSON(@json) end end diff --git a/tests/json_encoding_test.rb b/tests/json_encoding_test.rb index 080c3ec..86c4df8 100644 --- a/tests/json_encoding_test.rb +++ b/tests/json_encoding_test.rb @@ -1,13 +1,16 @@ +# encoding: utf-8 #frozen_string_literal: false + require 'test_helper' class JSONEncodingTest < Test::Unit::TestCase include JSON def setup - @utf_8 = '"© ≠ €!"' - @parsed = "© ≠ €!" - @generated = '"\u00a9 \u2260 \u20ac!"' + @utf_8 = '"© ≠ €!"' + @ascii_8bit = @utf_8.dup.force_encoding('ascii-8bit') + @parsed = "© ≠ €!" + @generated = '"\u00a9 \u2260 \u20ac!"' if String.method_defined?(:encode) @utf_16_data = @parsed.encode('utf-16be', 'utf-8') @utf_16be = @utf_8.encode('utf-16be', 'utf-8') @@ -25,6 +28,7 @@ class JSONEncodingTest < Test::Unit::TestCase end def test_parse + assert_equal @parsed, JSON.parse(@ascii_8bit) assert_equal @parsed, JSON.parse(@utf_8) assert_equal @parsed, JSON.parse(@utf_16be) assert_equal @parsed, JSON.parse(@utf_16le) @@ -34,15 +38,7 @@ class JSONEncodingTest < Test::Unit::TestCase def test_generate assert_equal @generated, JSON.generate(@parsed, :ascii_only => true) - if defined?(::Encoding) - assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true) - else - # XXX checking of correct utf8 data is not as strict (yet?) without - # :ascii_only - assert_raise(JSON::GeneratorError) do - JSON.generate(@utf_16_data, :ascii_only => true) - end - end + assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true) end def test_unicode diff --git a/tests/json_ext_parser_test.rb b/tests/json_ext_parser_test.rb index 761ae5e..c5a030e 100644 --- a/tests/json_ext_parser_test.rb +++ b/tests/json_ext_parser_test.rb @@ -1,8 +1,5 @@ #frozen_string_literal: false require 'test_helper' -require 'stringio' -require 'tempfile' -require 'ostruct' class JSONExtParserTest < Test::Unit::TestCase if defined?(JSON::Ext::Parser) diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index e310373..18b0833 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -135,7 +135,6 @@ EOT :array_nl => "\n", :ascii_only => false, :buffer_initial_length => 1024, - :quirks_mode => false, :depth => 0, :indent => " ", :max_nesting => 100, @@ -152,7 +151,6 @@ EOT :array_nl => "", :ascii_only => false, :buffer_initial_length => 1024, - :quirks_mode => false, :depth => 0, :indent => "", :max_nesting => 100, @@ -169,7 +167,6 @@ EOT :array_nl => "", :ascii_only => false, :buffer_initial_length => 1024, - :quirks_mode => false, :depth => 0, :indent => "", :max_nesting => 0, @@ -375,6 +372,5 @@ EOT assert_nothing_raised(SystemStackError) do assert_equal '["foo"]', JSON.generate([s.new('foo')]) end - end end diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 3ccf6b8..5120f09 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -1,4 +1,6 @@ -#frozen_string_literal: false +# encoding: utf-8 +# frozen_string_literal: false + require 'test_helper' require 'stringio' require 'tempfile' @@ -146,7 +148,7 @@ class JSONParserTest < Test::Unit::TestCase assert parse('NaN', :allow_nan => true).nan? assert parse('Infinity', :allow_nan => true).infinite? assert parse('-Infinity', :allow_nan => true).infinite? - assert_raise(JSON::ParserError) { parse('[ 1, ]', :quirks_mode => true) } + assert_raise(JSON::ParserError) { parse('[ 1, ]') } end def test_parse_some_strings diff --git a/tests/test_helper.rb b/tests/test_helper.rb index 752f5f5..9d3665d 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -17,3 +17,7 @@ begin require 'byebug' rescue LoadError end +if ENV['START_SIMPLECOV'].to_i == 1 + require 'simplecov' + SimpleCov.start +end diff --git a/tools/fuzz.rb b/tools/fuzz.rb index c0fae12..8261930 100755 --- a/tools/fuzz.rb +++ b/tools/fuzz.rb @@ -1,13 +1,5 @@ require 'json' -require 'iconv' -ISO_8859_1_TO_UTF8 = Iconv.new('utf-8', 'iso-8859-15') -class ::String - def to_utf8 - ISO_8859_1_TO_UTF8.iconv self - end -end - class Fuzzer def initialize(n, freqs = {}) sum = freqs.inject(0.0) { |s, x| s + x.last } @@ -25,7 +17,7 @@ class Fuzzer def random_string s = '' 30.times { s << @alpha[rand(@alpha.size)] } - s.to_utf8 + s end def pick |