json 1.7.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +18 -10
  4. data/{CHANGES → CHANGES.md} +217 -69
  5. data/Gemfile +11 -12
  6. data/{COPYING-json-jruby → LICENSE} +5 -6
  7. data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
  8. data/{README.rdoc → README.md} +188 -137
  9. data/Rakefile +52 -37
  10. data/VERSION +1 -1
  11. data/ext/json/ext/fbuffer/fbuffer.h +38 -7
  12. data/ext/json/ext/generator/depend +1 -0
  13. data/ext/json/ext/generator/extconf.rb +1 -10
  14. data/ext/json/ext/generator/generator.c +239 -133
  15. data/ext/json/ext/generator/generator.h +35 -26
  16. data/ext/json/ext/parser/depend +1 -0
  17. data/ext/json/ext/parser/extconf.rb +2 -9
  18. data/ext/json/ext/parser/parser.c +446 -514
  19. data/ext/json/ext/parser/parser.h +23 -9
  20. data/ext/json/ext/parser/parser.rl +177 -208
  21. data/ext/json/extconf.rb +2 -0
  22. data/java/src/json/ext/ByteListTranscoder.java +1 -2
  23. data/java/src/json/ext/Generator.java +49 -20
  24. data/java/src/json/ext/GeneratorMethods.java +1 -2
  25. data/java/src/json/ext/GeneratorService.java +1 -2
  26. data/java/src/json/ext/GeneratorState.java +25 -57
  27. data/java/src/json/ext/OptionsReader.java +5 -5
  28. data/java/src/json/ext/Parser.java +141 -419
  29. data/java/src/json/ext/Parser.rl +57 -128
  30. data/java/src/json/ext/ParserService.java +1 -2
  31. data/java/src/json/ext/RuntimeInfo.java +1 -6
  32. data/java/src/json/ext/StringDecoder.java +1 -2
  33. data/java/src/json/ext/StringEncoder.java +5 -0
  34. data/java/src/json/ext/Utils.java +1 -2
  35. data/json-java.gemspec +17 -2
  36. data/json.gemspec +0 -0
  37. data/json_pure.gemspec +25 -26
  38. data/lib/json.rb +3 -2
  39. data/lib/json/add/bigdecimal.rb +10 -2
  40. data/lib/json/add/complex.rb +9 -2
  41. data/lib/json/add/core.rb +1 -0
  42. data/lib/json/add/date.rb +1 -1
  43. data/lib/json/add/date_time.rb +1 -1
  44. data/lib/json/add/exception.rb +1 -1
  45. data/lib/json/add/ostruct.rb +3 -3
  46. data/lib/json/add/range.rb +1 -1
  47. data/lib/json/add/rational.rb +8 -2
  48. data/lib/json/add/regexp.rb +3 -3
  49. data/lib/json/add/set.rb +29 -0
  50. data/lib/json/add/struct.rb +1 -1
  51. data/lib/json/add/symbol.rb +1 -1
  52. data/lib/json/add/time.rb +6 -3
  53. data/lib/json/common.rb +51 -66
  54. data/lib/json/ext.rb +0 -6
  55. data/lib/json/generic_object.rb +36 -4
  56. data/lib/json/pure.rb +2 -8
  57. data/lib/json/pure/generator.rb +106 -119
  58. data/lib/json/pure/parser.rb +48 -88
  59. data/lib/json/version.rb +2 -1
  60. data/references/rfc7159.txt +899 -0
  61. data/tests/fixtures/fail18.json +1 -1
  62. data/tests/fixtures/obsolete_fail1.json +1 -0
  63. data/tests/{test_json_addition.rb → json_addition_test.rb} +53 -38
  64. data/tests/json_common_interface_test.rb +126 -0
  65. data/tests/json_encoding_test.rb +107 -0
  66. data/tests/json_ext_parser_test.rb +15 -0
  67. data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +5 -8
  68. data/tests/json_generator_test.rb +421 -0
  69. data/tests/json_generic_object_test.rb +82 -0
  70. data/tests/json_parser_test.rb +472 -0
  71. data/tests/json_string_matching_test.rb +38 -0
  72. data/tests/{setup_variant.rb → test_helper.rb} +6 -0
  73. data/tools/diff.sh +18 -0
  74. data/tools/fuzz.rb +1 -9
  75. metadata +49 -72
  76. data/COPYING +0 -58
  77. data/GPL +0 -340
  78. data/TODO +0 -1
  79. data/data/example.json +0 -1
  80. data/data/index.html +0 -38
  81. data/data/prototype.js +0 -4184
  82. data/tests/fixtures/fail1.json +0 -1
  83. data/tests/test_json.rb +0 -539
  84. data/tests/test_json_encoding.rb +0 -65
  85. data/tests/test_json_generate.rb +0 -251
  86. data/tests/test_json_generic_object.rb +0 -35
  87. data/tests/test_json_string_matching.rb +0 -40
  88. data/tests/test_json_unicode.rb +0 -72
@@ -1,8 +1,7 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
3
  *
4
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
- * for details.
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
6
5
  */
7
6
  package json.ext;
8
7
 
@@ -51,12 +50,12 @@ public class Parser extends RubyObject {
51
50
  private int maxNesting;
52
51
  private boolean allowNaN;
53
52
  private boolean symbolizeNames;
54
- private boolean quirksMode;
55
53
  private RubyClass objectClass;
56
54
  private RubyClass arrayClass;
55
+ private RubyClass decimalClass;
57
56
  private RubyHash match_string;
58
57
 
59
- private static final int DEFAULT_MAX_NESTING = 19;
58
+ private static final int DEFAULT_MAX_NESTING = 100;
60
59
 
61
60
  private static final ByteList JSON_MINUS_INFINITY = new ByteList(ByteList.plain("-Infinity"));
62
61
  // constant names in the JSON module containing those values
@@ -111,7 +110,7 @@ public class Parser extends RubyObject {
111
110
  * <dt><code>:max_nesting</code>
112
111
  * <dd>The maximum depth of nesting allowed in the parsed data
113
112
  * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>,
114
- * it defaults to 19.
113
+ * it defaults to 100.
115
114
  *
116
115
  * <dt><code>:allow_nan</code>
117
116
  * <dd>If set to <code>true</code>, allow <code>NaN</code>,
@@ -122,13 +121,9 @@ public class Parser extends RubyObject {
122
121
  * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
123
122
  * a JSON object. Otherwise strings are returned, which is also the default.
124
123
  *
125
- * <dt><code>:quirks_mode?</code>
126
- * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false
127
- * otherwise.
128
- *
129
124
  * <dt><code>:create_additions</code>
130
125
  * <dd>If set to <code>false</code>, the Parser doesn't create additions
131
- * even if a matchin class and <code>create_id</code> was found. This option
126
+ * even if a matching class and <code>create_id</code> was found. This option
132
127
  * defaults to <code>true</code>.
133
128
  *
134
129
  * <dt><code>:object_class</code>
@@ -137,9 +132,10 @@ public class Parser extends RubyObject {
137
132
  * <dt><code>:array_class</code>
138
133
  * <dd>Defaults to Array.
139
134
  *
140
- * <dt><code>:quirks_mode</code>
141
- * <dd>Enables quirks_mode for parser, that is for example parsing single
142
- * JSON values instead of documents is possible.
135
+ * <dt><code>:decimal_class</code>
136
+ * <dd>Specifies which class to use instead of the default (Float) when
137
+ * parsing decimal numbers. This class must accept a single string argument
138
+ * in its constructor.
143
139
  * </dl>
144
140
  */
145
141
  @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -162,15 +158,21 @@ public class Parser extends RubyObject {
162
158
  this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
163
159
  this.allowNaN = opts.getBool("allow_nan", false);
164
160
  this.symbolizeNames = opts.getBool("symbolize_names", false);
165
- this.quirksMode = opts.getBool("quirks_mode", false);
166
161
  this.createId = opts.getString("create_id", getCreateId(context));
167
- this.createAdditions = opts.getBool("create_additions", true);
162
+ this.createAdditions = opts.getBool("create_additions", false);
168
163
  this.objectClass = opts.getClass("object_class", runtime.getHash());
169
164
  this.arrayClass = opts.getClass("array_class", runtime.getArray());
165
+ this.decimalClass = opts.getClass("decimal_class", null);
170
166
  this.match_string = opts.getHash("match_string");
171
167
 
168
+ if(symbolizeNames && createAdditions) {
169
+ throw runtime.newArgumentError(
170
+ "options :symbolize_names and :create_additions cannot be " +
171
+ " used in conjunction"
172
+ );
173
+ }
172
174
  this.vSource = args[0].convertToString();
173
- if (!quirksMode) this.vSource = convertEncoding(context, vSource);
175
+ this.vSource = convertEncoding(context, vSource);
174
176
 
175
177
  return this;
176
178
  }
@@ -181,33 +183,16 @@ public class Parser extends RubyObject {
181
183
  * Returns the source string if no conversion is needed.
182
184
  */
183
185
  private RubyString convertEncoding(ThreadContext context, RubyString source) {
184
- ByteList bl = source.getByteList();
185
- int len = bl.length();
186
- if (len < 2) {
187
- throw Utils.newException(context, Utils.M_PARSER_ERROR,
188
- "A JSON text must at least contain two octets!");
189
- }
190
-
191
- if (info.encodingsSupported()) {
192
- RubyEncoding encoding = (RubyEncoding)source.encoding(context);
193
- if (encoding != info.ascii8bit.get()) {
194
- return (RubyString)source.encode(context, info.utf8.get());
195
- }
196
-
197
- String sniffedEncoding = sniffByteList(bl);
198
- if (sniffedEncoding == null) return source; // assume UTF-8
199
- return reinterpretEncoding(context, source, sniffedEncoding);
200
- }
201
-
202
- String sniffedEncoding = sniffByteList(bl);
203
- if (sniffedEncoding == null) return source; // assume UTF-8
204
- Ruby runtime = context.getRuntime();
205
- return (RubyString)info.jsonModule.get().
206
- callMethod(context, "iconv",
207
- new IRubyObject[] {
208
- runtime.newString("utf-8"),
209
- runtime.newString(sniffedEncoding),
210
- source});
186
+ RubyEncoding encoding = (RubyEncoding)source.encoding(context);
187
+ if (encoding == info.ascii8bit.get()) {
188
+ if (source.isFrozen()) {
189
+ source = (RubyString) source.dup();
190
+ }
191
+ source.force_encoding(context, info.utf8.get());
192
+ } else {
193
+ source = (RubyString) source.encode(context, info.utf8.get());
194
+ }
195
+ return source;
211
196
  }
212
197
 
213
198
  /**
@@ -246,7 +231,7 @@ public class Parser extends RubyObject {
246
231
  */
247
232
  @JRubyMethod
248
233
  public IRubyObject parse(ThreadContext context) {
249
- return new ParserSession(this, context).parse();
234
+ return new ParserSession(this, context, info).parse();
250
235
  }
251
236
 
252
237
  /**
@@ -260,17 +245,6 @@ public class Parser extends RubyObject {
260
245
  return checkAndGetSource().dup();
261
246
  }
262
247
 
263
- /**
264
- * <code>Parser#quirks_mode?()</code>
265
- *
266
- * <p>If set to <code>true</code>, if the parse is in quirks_mode, false
267
- * otherwise.
268
- */
269
- @JRubyMethod(name = "quirks_mode?")
270
- public IRubyObject quirks_mode_p(ThreadContext context) {
271
- return context.getRuntime().newBoolean(quirksMode);
272
- }
273
-
274
248
  public RubyString checkAndGetSource() {
275
249
  if (vSource != null) {
276
250
  return vSource;
@@ -302,6 +276,7 @@ public class Parser extends RubyObject {
302
276
  private static class ParserSession {
303
277
  private final Parser parser;
304
278
  private final ThreadContext context;
279
+ private final RuntimeInfo info;
305
280
  private final ByteList byteList;
306
281
  private final ByteList view;
307
282
  private final byte[] data;
@@ -313,9 +288,10 @@ public class Parser extends RubyObject {
313
288
  // no idea about the origins of this value, ask Flori ;)
314
289
  private static final int EVIL = 0x666;
315
290
 
316
- private ParserSession(Parser parser, ThreadContext context) {
291
+ private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) {
317
292
  this.parser = parser;
318
293
  this.context = context;
294
+ this.info = info;
319
295
  this.byteList = parser.checkAndGetSource().getByteList();
320
296
  this.data = byteList.unsafeBytes();
321
297
  this.view = new ByteList(data, false);
@@ -392,7 +368,7 @@ public class Parser extends RubyObject {
392
368
  }
393
369
  }
394
370
  action parse_number {
395
- if (pe > fpc + 9 - (parser.quirksMode ? 1 : 0) &&
371
+ if (pe > fpc + 8 &&
396
372
  absSubSequence(fpc, fpc + 9).equals(JSON_MINUS_INFINITY)) {
397
373
 
398
374
  if (parser.allowNaN) {
@@ -519,13 +495,13 @@ public class Parser extends RubyObject {
519
495
 
520
496
  return p;
521
497
  }
522
-
498
+
523
499
  RubyInteger createInteger(int p, int new_p) {
524
500
  Ruby runtime = getRuntime();
525
501
  ByteList num = absSubSequence(p, new_p);
526
502
  return bytesToInum(runtime, num);
527
503
  }
528
-
504
+
529
505
  RubyInteger bytesToInum(Ruby runtime, ByteList num) {
530
506
  return runtime.is1_9() ?
531
507
  ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -555,7 +531,9 @@ public class Parser extends RubyObject {
555
531
  res.update(null, p);
556
532
  return;
557
533
  }
558
- RubyFloat number = createFloat(p, new_p);
534
+ IRubyObject number = parser.decimalClass == null ?
535
+ createFloat(p, new_p) : createCustomDecimal(p, new_p);
536
+
559
537
  res.update(number, new_p + 1);
560
538
  return;
561
539
  }
@@ -570,16 +548,23 @@ public class Parser extends RubyObject {
570
548
  if (cs < JSON_float_first_final) {
571
549
  return -1;
572
550
  }
573
-
551
+
574
552
  return p;
575
553
  }
576
-
554
+
577
555
  RubyFloat createFloat(int p, int new_p) {
578
556
  Ruby runtime = getRuntime();
579
557
  ByteList num = absSubSequence(p, new_p);
580
558
  return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
581
559
  }
582
560
 
561
+ IRubyObject createCustomDecimal(int p, int new_p) {
562
+ Ruby runtime = getRuntime();
563
+ ByteList num = absSubSequence(p, new_p);
564
+ IRubyObject numString = runtime.newString(num.toString());
565
+ return parser.decimalClass.callMethod(context, "new", numString);
566
+ }
567
+
583
568
  %%{
584
569
  machine JSON_string;
585
570
  include JSON_common;
@@ -622,11 +607,11 @@ public class Parser extends RubyObject {
622
607
  %% write exec;
623
608
 
624
609
  if (parser.createAdditions) {
625
- RubyHash match_string = parser.match_string;
626
- if (match_string != null) {
610
+ RubyHash matchString = parser.match_string;
611
+ if (matchString != null) {
627
612
  final IRubyObject[] memoArray = { result, null };
628
613
  try {
629
- match_string.visitAll(new RubyHash.Visitor() {
614
+ matchString.visitAll(new RubyHash.Visitor() {
630
615
  @Override
631
616
  public void visit(IRubyObject pattern, IRubyObject klass) {
632
617
  if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
@@ -647,6 +632,9 @@ public class Parser extends RubyObject {
647
632
  }
648
633
 
649
634
  if (cs >= JSON_string_first_final && result != null) {
635
+ if (result instanceof RubyString) {
636
+ ((RubyString)result).force_encoding(context, info.utf8.get());
637
+ }
650
638
  res.update(result, p + 1);
651
639
  } else {
652
640
  res.update(null, p + 1);
@@ -761,7 +749,7 @@ public class Parser extends RubyObject {
761
749
  fhold;
762
750
  fbreak;
763
751
  }
764
-
752
+
765
753
  pair = ignore* begin_name >parse_name ignore* name_separator
766
754
  ignore* begin_value >parse_value;
767
755
  next_pair = ignore* value_separator pair;
@@ -831,60 +819,6 @@ public class Parser extends RubyObject {
831
819
 
832
820
  write data;
833
821
 
834
- action parse_object {
835
- currentNesting = 1;
836
- parseObject(res, fpc, pe);
837
- if (res.result == null) {
838
- fhold;
839
- fbreak;
840
- } else {
841
- result = res.result;
842
- fexec res.p;
843
- }
844
- }
845
-
846
- action parse_array {
847
- currentNesting = 1;
848
- parseArray(res, fpc, pe);
849
- if (res.result == null) {
850
- fhold;
851
- fbreak;
852
- } else {
853
- result = res.result;
854
- fexec res.p;
855
- }
856
- }
857
-
858
- main := ignore*
859
- ( begin_object >parse_object
860
- | begin_array >parse_array )
861
- ignore*;
862
- }%%
863
-
864
- public IRubyObject parseStrict() {
865
- int cs = EVIL;
866
- int p, pe;
867
- IRubyObject result = null;
868
- ParserResult res = new ParserResult();
869
-
870
- %% write init;
871
- p = byteList.begin();
872
- pe = p + byteList.length();
873
- %% write exec;
874
-
875
- if (cs >= JSON_first_final && p == pe) {
876
- return result;
877
- } else {
878
- throw unexpectedToken(p, pe);
879
- }
880
- }
881
-
882
- %%{
883
- machine JSON_quirks_mode;
884
- include JSON_common;
885
-
886
- write data;
887
-
888
822
  action parse_value {
889
823
  parseValue(res, fpc, pe);
890
824
  if (res.result == null) {
@@ -901,7 +835,7 @@ public class Parser extends RubyObject {
901
835
  ignore*;
902
836
  }%%
903
837
 
904
- public IRubyObject parseQuirksMode() {
838
+ public IRubyObject parseImplemetation() {
905
839
  int cs = EVIL;
906
840
  int p, pe;
907
841
  IRubyObject result = null;
@@ -912,7 +846,7 @@ public class Parser extends RubyObject {
912
846
  pe = p + byteList.length();
913
847
  %% write exec;
914
848
 
915
- if (cs >= JSON_quirks_mode_first_final && p == pe) {
849
+ if (cs >= JSON_first_final && p == pe) {
916
850
  return result;
917
851
  } else {
918
852
  throw unexpectedToken(p, pe);
@@ -920,12 +854,7 @@ public class Parser extends RubyObject {
920
854
  }
921
855
 
922
856
  public IRubyObject parse() {
923
- if (parser.quirksMode) {
924
- return parseQuirksMode();
925
- } else {
926
- return parseStrict();
927
- }
928
-
857
+ return parseImplemetation();
929
858
  }
930
859
 
931
860
  /**
@@ -1,8 +1,7 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
3
  *
4
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
- * for details.
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
6
5
  */
7
6
  package json.ext;
8
7
 
@@ -1,8 +1,7 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
3
  *
4
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
- * for details.
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
6
5
  */
7
6
  package json.ext;
8
7
 
@@ -91,10 +90,6 @@ final class RuntimeInfo {
91
90
  }
92
91
  }
93
92
 
94
- public boolean encodingsSupported() {
95
- return utf8 != null && utf8.get() != null;
96
- }
97
-
98
93
  public RubyEncoding getEncoding(ThreadContext context, String name) {
99
94
  synchronized (encodings) {
100
95
  WeakReference<RubyEncoding> encoding = encodings.get(name);
@@ -1,8 +1,7 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
3
  *
4
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
- * for details.
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
6
5
  */
7
6
  package json.ext;
8
7
 
@@ -1,3 +1,8 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
1
6
  package json.ext;
2
7
 
3
8
  import org.jruby.exceptions.RaiseException;
@@ -1,8 +1,7 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
3
  *
4
- * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
- * for details.
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
6
5
  */
7
6
  package json.ext;
8
7
 
data/json-java.gemspec CHANGED
@@ -8,11 +8,26 @@ spec = Gem::Specification.new do |s|
8
8
  s.description = "A JSON implementation as a JRuby extension."
9
9
  s.author = "Daniel Luz"
10
10
  s.email = "dev+ruby@mernen.com"
11
- s.homepage = "http://json-jruby.rubyforge.org/"
11
+ s.homepage = "http://flori.github.com/json"
12
12
  s.platform = 'java'
13
- s.rubyforge_project = "json-jruby"
13
+ s.licenses = ["Ruby"]
14
14
 
15
15
  s.files = Dir["{docs,lib,tests}/**/*"]
16
+
17
+ if s.respond_to? :specification_version then
18
+ s.specification_version = 4
19
+
20
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
21
+ s.add_development_dependency(%q<rake>, [">= 0"])
22
+ s.add_development_dependency(%q<test-unit>, ["~> 2.0"])
23
+ else
24
+ s.add_dependency(%q<rake>, [">= 0"])
25
+ s.add_dependency(%q<test-unit>, ["~> 2.0"])
26
+ end
27
+ else
28
+ s.add_dependency(%q<rake>, [">= 0"])
29
+ s.add_dependency(%q<test-unit>, ["~> 2.0"])
30
+ end
16
31
  end
17
32
 
18
33
  if $0 == __FILE__