json 1.8.3 → 2.4.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 (83) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/.travis.yml +9 -12
  4. data/{CHANGES → CHANGES.md} +219 -90
  5. data/Gemfile +10 -6
  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} +201 -134
  9. data/Rakefile +35 -113
  10. data/VERSION +1 -1
  11. data/ext/json/ext/fbuffer/fbuffer.h +0 -3
  12. data/ext/json/ext/generator/generator.c +255 -101
  13. data/ext/json/ext/generator/generator.h +12 -4
  14. data/ext/json/ext/parser/extconf.rb +28 -0
  15. data/ext/json/ext/parser/parser.c +410 -462
  16. data/ext/json/ext/parser/parser.h +5 -5
  17. data/ext/json/ext/parser/parser.rl +166 -181
  18. data/ext/json/extconf.rb +1 -1
  19. data/java/src/json/ext/ByteListTranscoder.java +1 -2
  20. data/java/src/json/ext/Generator.java +39 -36
  21. data/java/src/json/ext/GeneratorMethods.java +1 -2
  22. data/java/src/json/ext/GeneratorService.java +1 -2
  23. data/java/src/json/ext/GeneratorState.java +33 -56
  24. data/java/src/json/ext/OptionsReader.java +2 -3
  25. data/java/src/json/ext/Parser.java +146 -417
  26. data/java/src/json/ext/Parser.rl +62 -126
  27. data/java/src/json/ext/ParserService.java +1 -2
  28. data/java/src/json/ext/RuntimeInfo.java +1 -6
  29. data/java/src/json/ext/StringDecoder.java +1 -2
  30. data/java/src/json/ext/StringEncoder.java +13 -2
  31. data/java/src/json/ext/Utils.java +1 -2
  32. data/json-java.gemspec +22 -7
  33. data/json.gemspec +0 -0
  34. data/json_pure.gemspec +22 -29
  35. data/lib/json/add/bigdecimal.rb +3 -2
  36. data/lib/json/add/complex.rb +4 -4
  37. data/lib/json/add/core.rb +1 -0
  38. data/lib/json/add/date.rb +1 -1
  39. data/lib/json/add/date_time.rb +1 -1
  40. data/lib/json/add/exception.rb +1 -1
  41. data/lib/json/add/ostruct.rb +3 -3
  42. data/lib/json/add/range.rb +1 -1
  43. data/lib/json/add/rational.rb +3 -3
  44. data/lib/json/add/regexp.rb +3 -3
  45. data/lib/json/add/set.rb +29 -0
  46. data/lib/json/add/struct.rb +1 -1
  47. data/lib/json/add/symbol.rb +1 -1
  48. data/lib/json/add/time.rb +1 -1
  49. data/lib/json/common.rb +350 -152
  50. data/lib/json/ext.rb +0 -6
  51. data/lib/json/generic_object.rb +5 -4
  52. data/lib/json/pure/generator.rb +83 -126
  53. data/lib/json/pure/parser.rb +62 -84
  54. data/lib/json/pure.rb +2 -8
  55. data/lib/json/version.rb +2 -1
  56. data/lib/json.rb +550 -29
  57. data/references/rfc7159.txt +899 -0
  58. data/tests/fixtures/obsolete_fail1.json +1 -0
  59. data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
  60. data/tests/json_common_interface_test.rb +169 -0
  61. data/tests/json_encoding_test.rb +107 -0
  62. data/tests/json_ext_parser_test.rb +15 -0
  63. data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +13 -8
  64. data/tests/{test_json_generate.rb → json_generator_test.rb} +134 -39
  65. data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
  66. data/tests/json_parser_test.rb +497 -0
  67. data/tests/json_string_matching_test.rb +38 -0
  68. data/tests/test_helper.rb +17 -0
  69. data/tools/diff.sh +18 -0
  70. data/tools/fuzz.rb +1 -9
  71. metadata +47 -53
  72. data/COPYING +0 -58
  73. data/GPL +0 -340
  74. data/TODO +0 -1
  75. data/data/example.json +0 -1
  76. data/data/index.html +0 -38
  77. data/data/prototype.js +0 -4184
  78. data/tests/fixtures/fail1.json +0 -1
  79. data/tests/setup_variant.rb +0 -11
  80. data/tests/test_json.rb +0 -553
  81. data/tests/test_json_encoding.rb +0 -65
  82. data/tests/test_json_string_matching.rb +0 -39
  83. data/tests/test_json_unicode.rb +0 -72
@@ -1,20 +1,18 @@
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
 
9
8
  import org.jruby.Ruby;
10
9
  import org.jruby.RubyArray;
10
+ import org.jruby.RubyBasicObject;
11
11
  import org.jruby.RubyBignum;
12
12
  import org.jruby.RubyBoolean;
13
- import org.jruby.RubyClass;
14
13
  import org.jruby.RubyFixnum;
15
14
  import org.jruby.RubyFloat;
16
15
  import org.jruby.RubyHash;
17
- import org.jruby.RubyNumeric;
18
16
  import org.jruby.RubyString;
19
17
  import org.jruby.runtime.ThreadContext;
20
18
  import org.jruby.runtime.builtin.IRubyObject;
@@ -31,8 +29,7 @@ public final class Generator {
31
29
  static <T extends IRubyObject> RubyString
32
30
  generateJson(ThreadContext context, T object,
33
31
  Handler<? super T> handler, IRubyObject[] args) {
34
- Session session = new Session(context, args.length > 0 ? args[0]
35
- : null);
32
+ Session session = new Session(context, args.length > 0 ? args[0] : null);
36
33
  return session.infect(handler.generateNew(session, object));
37
34
  }
38
35
 
@@ -42,7 +39,7 @@ public final class Generator {
42
39
  */
43
40
  static <T extends IRubyObject> RubyString
44
41
  generateJson(ThreadContext context, T object, IRubyObject[] args) {
45
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
42
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
46
43
  return generateJson(context, object, handler, args);
47
44
  }
48
45
 
@@ -54,7 +51,7 @@ public final class Generator {
54
51
  generateJson(ThreadContext context, T object,
55
52
  GeneratorState config) {
56
53
  Session session = new Session(context, config);
57
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
54
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
58
55
  return handler.generateNew(session, object);
59
56
  }
60
57
 
@@ -64,18 +61,24 @@ public final class Generator {
64
61
  // Java's generics can't handle this satisfactorily, so I'll just leave
65
62
  // the best I could get and ignore the warnings
66
63
  @SuppressWarnings("unchecked")
67
- private static <T extends IRubyObject>
68
- Handler<? super T> getHandlerFor(Ruby runtime, T object) {
69
- RubyClass metaClass = object.getMetaClass();
70
- if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
71
- if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
72
- if (metaClass == runtime.getHash()) return (Handler)HASH_HANDLER;
73
- if (metaClass == runtime.getArray()) return (Handler)ARRAY_HANDLER;
74
- if (object.isNil()) return (Handler)NIL_HANDLER;
75
- if (object == runtime.getTrue()) return (Handler)TRUE_HANDLER;
76
- if (object == runtime.getFalse()) return (Handler)FALSE_HANDLER;
77
- if (metaClass == runtime.getFloat()) return (Handler)FLOAT_HANDLER;
78
- if (metaClass == runtime.getBignum()) return (Handler)BIGNUM_HANDLER;
64
+ private static <T extends IRubyObject> Handler<? super T> getHandlerFor(Ruby runtime, T object) {
65
+ switch (((RubyBasicObject) object).getNativeClassIndex()) {
66
+ case NIL : return (Handler) NIL_HANDLER;
67
+ case TRUE : return (Handler) TRUE_HANDLER;
68
+ case FALSE : return (Handler) FALSE_HANDLER;
69
+ case FLOAT : return (Handler) FLOAT_HANDLER;
70
+ case FIXNUM : return (Handler) FIXNUM_HANDLER;
71
+ case BIGNUM : return (Handler) BIGNUM_HANDLER;
72
+ case STRING :
73
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break;
74
+ return (Handler) STRING_HANDLER;
75
+ case ARRAY :
76
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break;
77
+ return (Handler) ARRAY_HANDLER;
78
+ case HASH :
79
+ if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break;
80
+ return (Handler) HASH_HANDLER;
81
+ }
79
82
  return GENERIC_HANDLER;
80
83
  }
81
84
 
@@ -136,7 +139,7 @@ public final class Generator {
136
139
 
137
140
  public StringEncoder getStringEncoder() {
138
141
  if (stringEncoder == null) {
139
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
142
+ stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash());
140
143
  }
141
144
  return stringEncoder;
142
145
  }
@@ -173,9 +176,7 @@ public final class Generator {
173
176
  result = RubyString.newString(session.getRuntime(), buffer);
174
177
  ThreadContext context = session.getContext();
175
178
  RuntimeInfo info = session.getInfo();
176
- if (info.encodingsSupported()) {
177
- result.force_encoding(context, info.utf8.get());
178
- }
179
+ result.force_encoding(context, info.utf8.get());
179
180
  return result;
180
181
  }
181
182
 
@@ -333,13 +334,13 @@ public final class Generator {
333
334
 
334
335
  buffer.append((byte)'{');
335
336
  buffer.append(objectNl);
336
- object.visitAll(new RubyHash.Visitor() {
337
- private boolean firstPair = true;
338
337
 
338
+ final boolean[] firstPair = new boolean[]{true};
339
+ object.visitAll(new RubyHash.Visitor() {
339
340
  @Override
340
341
  public void visit(IRubyObject key, IRubyObject value) {
341
- if (firstPair) {
342
- firstPair = false;
342
+ if (firstPair[0]) {
343
+ firstPair[0] = false;
343
344
  } else {
344
345
  buffer.append((byte)',');
345
346
  buffer.append(objectNl);
@@ -359,7 +360,7 @@ public final class Generator {
359
360
  }
360
361
  });
361
362
  state.decreaseDepth();
362
- if (objectNl.length() != 0) {
363
+ if (!firstPair[0] && objectNl.length() != 0) {
363
364
  buffer.append(objectNl);
364
365
  buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
365
366
  }
@@ -382,8 +383,7 @@ public final class Generator {
382
383
  RuntimeInfo info = session.getInfo();
383
384
  RubyString src;
384
385
 
385
- if (info.encodingsSupported() &&
386
- object.encoding(session.getContext()) != info.utf8.get()) {
386
+ if (object.encoding(session.getContext()) != info.utf8.get()) {
387
387
  src = (RubyString)object.encode(session.getContext(),
388
388
  info.utf8.get());
389
389
  } else {
@@ -428,11 +428,14 @@ public final class Generator {
428
428
  new Handler<IRubyObject>() {
429
429
  @Override
430
430
  RubyString generateNew(Session session, IRubyObject object) {
431
- IRubyObject result =
432
- object.callMethod(session.getContext(), "to_json",
433
- new IRubyObject[] {session.getState()});
434
- if (result instanceof RubyString) return (RubyString)result;
435
- throw session.getRuntime().newTypeError("to_json must return a String");
431
+ if (object.respondsTo("to_json")) {
432
+ IRubyObject result = object.callMethod(session.getContext(), "to_json",
433
+ new IRubyObject[] {session.getState()});
434
+ if (result instanceof RubyString) return (RubyString)result;
435
+ throw session.getRuntime().newTypeError("to_json must return a String");
436
+ } else {
437
+ return OBJECT_HANDLER.generateNew(session, object);
438
+ }
436
439
  }
437
440
 
438
441
  @Override
@@ -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
 
@@ -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
 
@@ -83,6 +82,12 @@ public class GeneratorState extends RubyObject {
83
82
  */
84
83
  private boolean quirksMode = DEFAULT_QUIRKS_MODE;
85
84
  static final boolean DEFAULT_QUIRKS_MODE = false;
85
+ /**
86
+ * If set to <code>true</code> the forward slash will be escaped in
87
+ * json output.
88
+ */
89
+ private boolean escapeSlash = DEFAULT_ESCAPE_SLASH;
90
+ static final boolean DEFAULT_ESCAPE_SLASH = false;
86
91
  /**
87
92
  * The initial buffer length of this state. (This isn't really used on all
88
93
  * non-C implementations.)
@@ -172,6 +177,9 @@ public class GeneratorState extends RubyObject {
172
177
  * <code>-Infinity</code> should be generated, otherwise an exception is
173
178
  * thrown if these values are encountered.
174
179
  * This options defaults to <code>false</code>.
180
+ * <dt><code>:escape_slash</code>
181
+ * <dd>set to <code>true</code> if the forward slashes should be escaped
182
+ * in the json output (default: <code>false</code>)
175
183
  */
176
184
  @JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
177
185
  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
@@ -195,6 +203,7 @@ public class GeneratorState extends RubyObject {
195
203
  this.allowNaN = orig.allowNaN;
196
204
  this.asciiOnly = orig.asciiOnly;
197
205
  this.quirksMode = orig.quirksMode;
206
+ this.escapeSlash = orig.escapeSlash;
198
207
  this.bufferInitialLength = orig.bufferInitialLength;
199
208
  this.depth = orig.depth;
200
209
  return this;
@@ -208,45 +217,11 @@ public class GeneratorState extends RubyObject {
208
217
  @JRubyMethod
209
218
  public IRubyObject generate(ThreadContext context, IRubyObject obj) {
210
219
  RubyString result = Generator.generateJson(context, obj, this);
211
- if (!quirksMode && !objectOrArrayLiteral(result)) {
212
- throw Utils.newException(context, Utils.M_GENERATOR_ERROR,
213
- "only generation of JSON objects or arrays allowed");
214
- }
215
220
  RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
216
- if (info.encodingsSupported()) {
217
- result.force_encoding(context, info.utf8.get());
218
- }
221
+ result.force_encoding(context, info.utf8.get());
219
222
  return result;
220
223
  }
221
224
 
222
- /**
223
- * Ensures the given string is in the form "[...]" or "{...}", being
224
- * possibly surrounded by white space.
225
- * The string's encoding must be ASCII-compatible.
226
- * @param value
227
- * @return
228
- */
229
- private static boolean objectOrArrayLiteral(RubyString value) {
230
- ByteList bl = value.getByteList();
231
- int len = bl.length();
232
-
233
- for (int pos = 0; pos < len - 1; pos++) {
234
- int b = bl.get(pos);
235
- if (Character.isWhitespace(b)) continue;
236
-
237
- // match the opening brace
238
- switch (b) {
239
- case '[':
240
- return matchClosingBrace(bl, pos, len, ']');
241
- case '{':
242
- return matchClosingBrace(bl, pos, len, '}');
243
- default:
244
- return false;
245
- }
246
- }
247
- return false;
248
- }
249
-
250
225
  private static boolean matchClosingBrace(ByteList bl, int pos, int len,
251
226
  int brace) {
252
227
  for (int endPos = len - 1; endPos > pos; endPos--) {
@@ -381,6 +356,24 @@ public class GeneratorState extends RubyObject {
381
356
  return max_nesting;
382
357
  }
383
358
 
359
+ /**
360
+ * Returns true if forward slashes are escaped in the json output.
361
+ */
362
+ public boolean escapeSlash() {
363
+ return escapeSlash;
364
+ }
365
+
366
+ @JRubyMethod(name="escape_slash")
367
+ public RubyBoolean escape_slash_get(ThreadContext context) {
368
+ return context.getRuntime().newBoolean(escapeSlash);
369
+ }
370
+
371
+ @JRubyMethod(name="escape_slash=")
372
+ public IRubyObject escape_slash_set(IRubyObject escape_slash) {
373
+ escapeSlash = escape_slash.isTrue();
374
+ return escape_slash.getRuntime().newBoolean(escapeSlash);
375
+ }
376
+
384
377
  public boolean allowNaN() {
385
378
  return allowNaN;
386
379
  }
@@ -399,17 +392,6 @@ public class GeneratorState extends RubyObject {
399
392
  return context.getRuntime().newBoolean(asciiOnly);
400
393
  }
401
394
 
402
- @JRubyMethod(name="quirks_mode")
403
- public RubyBoolean quirks_mode_get(ThreadContext context) {
404
- return context.getRuntime().newBoolean(quirksMode);
405
- }
406
-
407
- @JRubyMethod(name="quirks_mode=")
408
- public IRubyObject quirks_mode_set(IRubyObject quirks_mode) {
409
- quirksMode = quirks_mode.isTrue();
410
- return quirks_mode.getRuntime().newBoolean(quirksMode);
411
- }
412
-
413
395
  @JRubyMethod(name="buffer_initial_length")
414
396
  public RubyInteger buffer_initial_length_get(ThreadContext context) {
415
397
  return context.getRuntime().newFixnum(bufferInitialLength);
@@ -422,11 +404,6 @@ public class GeneratorState extends RubyObject {
422
404
  return buffer_initial_length;
423
405
  }
424
406
 
425
- @JRubyMethod(name="quirks_mode?")
426
- public RubyBoolean quirks_mode_p(ThreadContext context) {
427
- return context.getRuntime().newBoolean(quirksMode);
428
- }
429
-
430
407
  public int getDepth() {
431
408
  return depth;
432
409
  }
@@ -445,7 +422,7 @@ public class GeneratorState extends RubyObject {
445
422
  private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
446
423
  RubyString str = value.convertToString();
447
424
  RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
448
- if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
425
+ if (str.encoding(context) != info.utf8.get()) {
449
426
  str = (RubyString)str.encode(context, info.utf8.get());
450
427
  }
451
428
  return str.getByteList().dup();
@@ -481,7 +458,7 @@ public class GeneratorState extends RubyObject {
481
458
  maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
482
459
  allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN);
483
460
  asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
484
- quirksMode = opts.getBool("quirks_mode", DEFAULT_QUIRKS_MODE);
461
+ escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH);
485
462
  bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
486
463
 
487
464
  depth = opts.getInt("depth", 0);
@@ -508,8 +485,8 @@ public class GeneratorState extends RubyObject {
508
485
  result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context));
509
486
  result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
510
487
  result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
511
- result.op_aset(context, runtime.newSymbol("quirks_mode"), quirks_mode_p(context));
512
488
  result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context));
489
+ result.op_aset(context, runtime.newSymbol("escape_slash"), escape_slash_get(context));
513
490
  result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
514
491
  result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
515
492
  for (String name: getInstanceVariableNameList()) {
@@ -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
 
@@ -85,7 +84,7 @@ final class OptionsReader {
85
84
 
86
85
  RubyString str = value.convertToString();
87
86
  RuntimeInfo info = getRuntimeInfo();
88
- if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
87
+ if (str.encoding(context) != info.utf8.get()) {
89
88
  str = (RubyString)str.encode(context, info.utf8.get());
90
89
  }
91
90
  return str;