json_pure 2.0.4 → 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 (46) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -5
  4. data/CHANGES.md +39 -0
  5. data/Gemfile +1 -3
  6. data/LICENSE +56 -0
  7. data/README.md +54 -21
  8. data/Rakefile +19 -93
  9. data/VERSION +1 -1
  10. data/ext/json/ext/generator/generator.c +214 -45
  11. data/ext/json/ext/generator/generator.h +5 -2
  12. data/ext/json/ext/parser/extconf.rb +25 -0
  13. data/ext/json/ext/parser/parser.c +155 -83
  14. data/ext/json/ext/parser/parser.h +2 -0
  15. data/ext/json/ext/parser/parser.rl +79 -7
  16. data/ext/json/extconf.rb +1 -0
  17. data/java/src/json/ext/Generator.java +28 -24
  18. data/java/src/json/ext/GeneratorState.java +30 -0
  19. data/java/src/json/ext/Parser.java +109 -82
  20. data/java/src/json/ext/Parser.rl +39 -12
  21. data/java/src/json/ext/StringEncoder.java +8 -2
  22. data/json-java.gemspec +22 -22
  23. data/json.gemspec +0 -0
  24. data/json_pure.gemspec +9 -14
  25. data/lib/json.rb +549 -29
  26. data/lib/json/add/bigdecimal.rb +2 -2
  27. data/lib/json/add/complex.rb +2 -3
  28. data/lib/json/add/ostruct.rb +1 -1
  29. data/lib/json/add/rational.rb +2 -3
  30. data/lib/json/add/regexp.rb +2 -2
  31. data/lib/json/add/set.rb +29 -0
  32. data/lib/json/common.rb +341 -115
  33. data/lib/json/pure/generator.rb +31 -10
  34. data/lib/json/pure/parser.rb +35 -5
  35. data/lib/json/version.rb +1 -1
  36. data/tests/json_addition_test.rb +6 -0
  37. data/tests/json_common_interface_test.rb +47 -4
  38. data/tests/json_encoding_test.rb +2 -2
  39. data/tests/json_fixtures_test.rb +9 -1
  40. data/tests/json_generator_test.rb +55 -0
  41. data/tests/json_parser_test.rb +43 -12
  42. data/tests/test_helper.rb +3 -7
  43. metadata +17 -13
  44. data/data/example.json +0 -1
  45. data/data/index.html +0 -38
  46. data/data/prototype.js +0 -4184
@@ -37,8 +37,10 @@ typedef struct JSON_ParserStruct {
37
37
  int allow_nan;
38
38
  int parsing_name;
39
39
  int symbolize_names;
40
+ int freeze;
40
41
  VALUE object_class;
41
42
  VALUE array_class;
43
+ VALUE decimal_class;
42
44
  int create_additions;
43
45
  VALUE match_string;
44
46
  FBuffer *fbuffer;
@@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
25
25
 
26
26
  /* unicode */
27
27
 
28
- static const char digit_values[256] = {
28
+ static const signed char digit_values[256] = {
29
29
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30
30
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31
31
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
@@ -44,7 +44,7 @@ static const char digit_values[256] = {
44
44
 
45
45
  static UTF32 unescape_unicode(const unsigned char *p)
46
46
  {
47
- char b;
47
+ signed char b;
48
48
  UTF32 result = 0;
49
49
  b = digit_values[p[0]];
50
50
  if (b < 0) return UNI_REPLACEMENT_CHAR;
@@ -89,11 +89,13 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
89
89
 
90
90
  static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
91
91
  static VALUE CNaN, CInfinity, CMinusInfinity;
92
+ static VALUE cBigDecimal = Qundef;
92
93
 
93
94
  static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
94
95
  i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
95
- i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match,
96
- i_match_string, i_aset, i_aref, i_leftshift;
96
+ i_object_class, i_array_class, i_decimal_class, i_key_p,
97
+ i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
98
+ i_leftshift, i_new, i_BigDecimal, i_freeze, i_uminus;
97
99
 
98
100
  %%{
99
101
  machine JSON_common;
@@ -136,6 +138,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
136
138
  fhold; fbreak;
137
139
  } else {
138
140
  if (NIL_P(json->object_class)) {
141
+ OBJ_FREEZE(last_name);
139
142
  rb_hash_aset(*result, last_name, v);
140
143
  } else {
141
144
  rb_funcall(*result, i_aset, 2, last_name, v);
@@ -287,6 +290,10 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
287
290
  %% write init;
288
291
  %% write exec;
289
292
 
293
+ if (json->freeze) {
294
+ OBJ_FREEZE(*result);
295
+ }
296
+
290
297
  if (cs >= JSON_value_first_final) {
291
298
  return p;
292
299
  } else {
@@ -338,6 +345,19 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
338
345
  ) (^[0-9Ee.\-]? @exit );
339
346
  }%%
340
347
 
348
+ static int is_bigdecimal_class(VALUE obj)
349
+ {
350
+ if (cBigDecimal == Qundef) {
351
+ if (rb_const_defined(rb_cObject, i_BigDecimal)) {
352
+ cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
353
+ }
354
+ else {
355
+ return 0;
356
+ }
357
+ }
358
+ return obj == cBigDecimal;
359
+ }
360
+
341
361
  static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
342
362
  {
343
363
  int cs = EVIL;
@@ -351,7 +371,17 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
351
371
  fbuffer_clear(json->fbuffer);
352
372
  fbuffer_append(json->fbuffer, json->memo, len);
353
373
  fbuffer_append_char(json->fbuffer, '\0');
354
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
374
+ if (NIL_P(json->decimal_class)) {
375
+ *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
376
+ } else {
377
+ VALUE text;
378
+ text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
379
+ if (is_bigdecimal_class(json->decimal_class)) {
380
+ *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
381
+ } else {
382
+ *result = rb_funcall(json->decimal_class, i_new, 1, text);
383
+ }
384
+ }
355
385
  return p + 1;
356
386
  } else {
357
387
  return NULL;
@@ -546,8 +576,23 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
546
576
 
547
577
  if (json->symbolize_names && json->parsing_name) {
548
578
  *result = rb_str_intern(*result);
549
- } else {
579
+ } else if (RB_TYPE_P(*result, T_STRING)) {
580
+ # if STR_UMINUS_DEDUPE_FROZEN
581
+ if (json->freeze) {
582
+ // Starting from MRI 2.8 it is preferable to freeze the string
583
+ // before deduplication so that it can be interned directly
584
+ // otherwise it would be duplicated first which is wasteful.
585
+ *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0);
586
+ }
587
+ # elif STR_UMINUS_DEDUPE
588
+ if (json->freeze) {
589
+ // MRI 2.5 and older do not deduplicate strings that are already
590
+ // frozen.
591
+ *result = rb_funcall(*result, i_uminus, 0);
592
+ }
593
+ # else
550
594
  rb_str_resize(*result, RSTRING_LEN(*result));
595
+ # endif
551
596
  }
552
597
  if (cs >= JSON_string_first_final) {
553
598
  return p + 1;
@@ -655,6 +700,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
655
700
  } else {
656
701
  json->symbolize_names = 0;
657
702
  }
703
+ tmp = ID2SYM(i_freeze);
704
+ if (option_given_p(opts, tmp)) {
705
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
706
+ } else {
707
+ json->freeze = 0;
708
+ }
658
709
  tmp = ID2SYM(i_create_additions);
659
710
  if (option_given_p(opts, tmp)) {
660
711
  json->create_additions = RTEST(rb_hash_aref(opts, tmp));
@@ -684,6 +735,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
684
735
  } else {
685
736
  json->array_class = Qnil;
686
737
  }
738
+ tmp = ID2SYM(i_decimal_class);
739
+ if (option_given_p(opts, tmp)) {
740
+ json->decimal_class = rb_hash_aref(opts, tmp);
741
+ } else {
742
+ json->decimal_class = Qnil;
743
+ }
687
744
  tmp = ID2SYM(i_match_string);
688
745
  if (option_given_p(opts, tmp)) {
689
746
  VALUE match_string = rb_hash_aref(opts, tmp);
@@ -697,10 +754,11 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
697
754
  } else {
698
755
  json->max_nesting = 100;
699
756
  json->allow_nan = 0;
700
- json->create_additions = 1;
757
+ json->create_additions = 0;
701
758
  json->create_id = rb_funcall(mJSON, i_create_id, 0);
702
759
  json->object_class = Qnil;
703
760
  json->array_class = Qnil;
761
+ json->decimal_class = Qnil;
704
762
  }
705
763
  source = convert_encoding(StringValue(source));
706
764
  StringValue(source);
@@ -760,6 +818,7 @@ static void JSON_mark(void *ptr)
760
818
  rb_gc_mark_maybe(json->create_id);
761
819
  rb_gc_mark_maybe(json->object_class);
762
820
  rb_gc_mark_maybe(json->array_class);
821
+ rb_gc_mark_maybe(json->decimal_class);
763
822
  rb_gc_mark_maybe(json->match_string);
764
823
  }
765
824
 
@@ -809,20 +868,28 @@ static VALUE cParser_source(VALUE self)
809
868
 
810
869
  void Init_parser(void)
811
870
  {
871
+ #undef rb_intern
812
872
  rb_require("json/common");
813
873
  mJSON = rb_define_module("JSON");
814
874
  mExt = rb_define_module_under(mJSON, "Ext");
815
875
  cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
816
876
  eParserError = rb_path2class("JSON::ParserError");
817
877
  eNestingError = rb_path2class("JSON::NestingError");
878
+ rb_gc_register_mark_object(eParserError);
879
+ rb_gc_register_mark_object(eNestingError);
818
880
  rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
819
881
  rb_define_method(cParser, "initialize", cParser_initialize, -1);
820
882
  rb_define_method(cParser, "parse", cParser_parse, 0);
821
883
  rb_define_method(cParser, "source", cParser_source, 0);
822
884
 
823
885
  CNaN = rb_const_get(mJSON, rb_intern("NaN"));
886
+ rb_gc_register_mark_object(CNaN);
887
+
824
888
  CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
889
+ rb_gc_register_mark_object(CInfinity);
890
+
825
891
  CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
892
+ rb_gc_register_mark_object(CMinusInfinity);
826
893
 
827
894
  i_json_creatable_p = rb_intern("json_creatable?");
828
895
  i_json_create = rb_intern("json_create");
@@ -834,6 +901,7 @@ void Init_parser(void)
834
901
  i_symbolize_names = rb_intern("symbolize_names");
835
902
  i_object_class = rb_intern("object_class");
836
903
  i_array_class = rb_intern("array_class");
904
+ i_decimal_class = rb_intern("decimal_class");
837
905
  i_match = rb_intern("match");
838
906
  i_match_string = rb_intern("match_string");
839
907
  i_key_p = rb_intern("key?");
@@ -841,6 +909,10 @@ void Init_parser(void)
841
909
  i_aset = rb_intern("[]=");
842
910
  i_aref = rb_intern("[]");
843
911
  i_leftshift = rb_intern("<<");
912
+ i_new = rb_intern("new");
913
+ i_BigDecimal = rb_intern("BigDecimal");
914
+ i_freeze = rb_intern("freeze");
915
+ i_uminus = rb_intern("-@");
844
916
  }
845
917
 
846
918
  /*
@@ -1,2 +1,3 @@
1
1
  require 'mkmf'
2
+
2
3
  create_makefile('json')
@@ -7,13 +7,12 @@ package json.ext;
7
7
 
8
8
  import org.jruby.Ruby;
9
9
  import org.jruby.RubyArray;
10
+ import org.jruby.RubyBasicObject;
10
11
  import org.jruby.RubyBignum;
11
12
  import org.jruby.RubyBoolean;
12
- import org.jruby.RubyClass;
13
13
  import org.jruby.RubyFixnum;
14
14
  import org.jruby.RubyFloat;
15
15
  import org.jruby.RubyHash;
16
- import org.jruby.RubyNumeric;
17
16
  import org.jruby.RubyString;
18
17
  import org.jruby.runtime.ThreadContext;
19
18
  import org.jruby.runtime.builtin.IRubyObject;
@@ -30,8 +29,7 @@ public final class Generator {
30
29
  static <T extends IRubyObject> RubyString
31
30
  generateJson(ThreadContext context, T object,
32
31
  Handler<? super T> handler, IRubyObject[] args) {
33
- Session session = new Session(context, args.length > 0 ? args[0]
34
- : null);
32
+ Session session = new Session(context, args.length > 0 ? args[0] : null);
35
33
  return session.infect(handler.generateNew(session, object));
36
34
  }
37
35
 
@@ -41,7 +39,7 @@ public final class Generator {
41
39
  */
42
40
  static <T extends IRubyObject> RubyString
43
41
  generateJson(ThreadContext context, T object, IRubyObject[] args) {
44
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
42
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
45
43
  return generateJson(context, object, handler, args);
46
44
  }
47
45
 
@@ -53,7 +51,7 @@ public final class Generator {
53
51
  generateJson(ThreadContext context, T object,
54
52
  GeneratorState config) {
55
53
  Session session = new Session(context, config);
56
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
54
+ Handler<? super T> handler = getHandlerFor(context.runtime, object);
57
55
  return handler.generateNew(session, object);
58
56
  }
59
57
 
@@ -63,18 +61,24 @@ public final class Generator {
63
61
  // Java's generics can't handle this satisfactorily, so I'll just leave
64
62
  // the best I could get and ignore the warnings
65
63
  @SuppressWarnings("unchecked")
66
- private static <T extends IRubyObject>
67
- Handler<? super T> getHandlerFor(Ruby runtime, T object) {
68
- RubyClass metaClass = object.getMetaClass();
69
- if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
70
- if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
71
- if (metaClass == runtime.getHash()) return (Handler)HASH_HANDLER;
72
- if (metaClass == runtime.getArray()) return (Handler)ARRAY_HANDLER;
73
- if (object.isNil()) return (Handler)NIL_HANDLER;
74
- if (object == runtime.getTrue()) return (Handler)TRUE_HANDLER;
75
- if (object == runtime.getFalse()) return (Handler)FALSE_HANDLER;
76
- if (metaClass == runtime.getFloat()) return (Handler)FLOAT_HANDLER;
77
- 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
+ }
78
82
  return GENERIC_HANDLER;
79
83
  }
80
84
 
@@ -135,7 +139,7 @@ public final class Generator {
135
139
 
136
140
  public StringEncoder getStringEncoder() {
137
141
  if (stringEncoder == null) {
138
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
142
+ stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().escapeSlash());
139
143
  }
140
144
  return stringEncoder;
141
145
  }
@@ -330,13 +334,13 @@ public final class Generator {
330
334
 
331
335
  buffer.append((byte)'{');
332
336
  buffer.append(objectNl);
333
- object.visitAll(new RubyHash.Visitor() {
334
- private boolean firstPair = true;
335
337
 
338
+ final boolean[] firstPair = new boolean[]{true};
339
+ object.visitAll(new RubyHash.Visitor() {
336
340
  @Override
337
341
  public void visit(IRubyObject key, IRubyObject value) {
338
- if (firstPair) {
339
- firstPair = false;
342
+ if (firstPair[0]) {
343
+ firstPair[0] = false;
340
344
  } else {
341
345
  buffer.append((byte)',');
342
346
  buffer.append(objectNl);
@@ -356,7 +360,7 @@ public final class Generator {
356
360
  }
357
361
  });
358
362
  state.decreaseDepth();
359
- if (objectNl.length() != 0) {
363
+ if (!firstPair[0] && objectNl.length() != 0) {
360
364
  buffer.append(objectNl);
361
365
  buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
362
366
  }
@@ -82,6 +82,12 @@ public class GeneratorState extends RubyObject {
82
82
  */
83
83
  private boolean quirksMode = DEFAULT_QUIRKS_MODE;
84
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;
85
91
  /**
86
92
  * The initial buffer length of this state. (This isn't really used on all
87
93
  * non-C implementations.)
@@ -171,6 +177,9 @@ public class GeneratorState extends RubyObject {
171
177
  * <code>-Infinity</code> should be generated, otherwise an exception is
172
178
  * thrown if these values are encountered.
173
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>)
174
183
  */
175
184
  @JRubyMethod(optional=1, visibility=Visibility.PRIVATE)
176
185
  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
@@ -194,6 +203,7 @@ public class GeneratorState extends RubyObject {
194
203
  this.allowNaN = orig.allowNaN;
195
204
  this.asciiOnly = orig.asciiOnly;
196
205
  this.quirksMode = orig.quirksMode;
206
+ this.escapeSlash = orig.escapeSlash;
197
207
  this.bufferInitialLength = orig.bufferInitialLength;
198
208
  this.depth = orig.depth;
199
209
  return this;
@@ -346,6 +356,24 @@ public class GeneratorState extends RubyObject {
346
356
  return max_nesting;
347
357
  }
348
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
+
349
377
  public boolean allowNaN() {
350
378
  return allowNaN;
351
379
  }
@@ -430,6 +458,7 @@ public class GeneratorState extends RubyObject {
430
458
  maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
431
459
  allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN);
432
460
  asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY);
461
+ escapeSlash = opts.getBool("escape_slash", DEFAULT_ESCAPE_SLASH);
433
462
  bufferInitialLength = opts.getInt("buffer_initial_length", DEFAULT_BUFFER_INITIAL_LENGTH);
434
463
 
435
464
  depth = opts.getInt("depth", 0);
@@ -457,6 +486,7 @@ public class GeneratorState extends RubyObject {
457
486
  result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context));
458
487
  result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context));
459
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));
460
490
  result.op_aset(context, runtime.newSymbol("depth"), depth_get(context));
461
491
  result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context));
462
492
  for (String name: getInstanceVariableNameList()) {
@@ -52,9 +52,11 @@ public class Parser extends RubyObject {
52
52
  private int maxNesting;
53
53
  private boolean allowNaN;
54
54
  private boolean symbolizeNames;
55
+ private boolean freeze;
55
56
  private RubyClass objectClass;
56
57
  private RubyClass arrayClass;
57
- private RubyHash matchString;
58
+ private RubyClass decimalClass;
59
+ private RubyHash match_string;
58
60
 
59
61
  private static final int DEFAULT_MAX_NESTING = 100;
60
62
 
@@ -133,6 +135,10 @@ public class Parser extends RubyObject {
133
135
  * <dt><code>:array_class</code>
134
136
  * <dd>Defaults to Array.
135
137
  *
138
+ * <dt><code>:decimal_class</code>
139
+ * <dd>Specifies which class to use instead of the default (Float) when
140
+ * parsing decimal numbers. This class must accept a single string argument
141
+ * in its constructor.
136
142
  * </dl>
137
143
  */
138
144
  @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -155,11 +161,13 @@ public class Parser extends RubyObject {
155
161
  this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
156
162
  this.allowNaN = opts.getBool("allow_nan", false);
157
163
  this.symbolizeNames = opts.getBool("symbolize_names", false);
164
+ this.freeze = opts.getBool("freeze", false);
158
165
  this.createId = opts.getString("create_id", getCreateId(context));
159
166
  this.createAdditions = opts.getBool("create_additions", false);
160
167
  this.objectClass = opts.getClass("object_class", runtime.getHash());
161
168
  this.arrayClass = opts.getClass("array_class", runtime.getArray());
162
- this.matchString = opts.getHash("match_string");
169
+ this.decimalClass = opts.getClass("decimal_class", null);
170
+ this.match_string = opts.getHash("match_string");
163
171
 
164
172
  if(symbolizeNames && createAdditions) {
165
173
  throw runtime.newArgumentError(
@@ -307,11 +315,11 @@ public class Parser extends RubyObject {
307
315
  }
308
316
 
309
317
 
310
- // line 333 "Parser.rl"
318
+ // line 341 "Parser.rl"
311
319
 
312
320
 
313
321
 
314
- // line 315 "Parser.java"
322
+ // line 323 "Parser.java"
315
323
  private static byte[] init__JSON_value_actions_0()
316
324
  {
317
325
  return new byte [] {
@@ -425,7 +433,7 @@ static final int JSON_value_error = 0;
425
433
  static final int JSON_value_en_main = 1;
426
434
 
427
435
 
428
- // line 439 "Parser.rl"
436
+ // line 447 "Parser.rl"
429
437
 
430
438
 
431
439
  void parseValue(ParserResult res, int p, int pe) {
@@ -433,14 +441,14 @@ static final int JSON_value_en_main = 1;
433
441
  IRubyObject result = null;
434
442
 
435
443
 
436
- // line 437 "Parser.java"
444
+ // line 445 "Parser.java"
437
445
  {
438
446
  cs = JSON_value_start;
439
447
  }
440
448
 
441
- // line 446 "Parser.rl"
449
+ // line 454 "Parser.rl"
442
450
 
443
- // line 444 "Parser.java"
451
+ // line 452 "Parser.java"
444
452
  {
445
453
  int _klen;
446
454
  int _trans = 0;
@@ -466,13 +474,13 @@ case 1:
466
474
  while ( _nacts-- > 0 ) {
467
475
  switch ( _JSON_value_actions[_acts++] ) {
468
476
  case 9:
469
- // line 424 "Parser.rl"
477
+ // line 432 "Parser.rl"
470
478
  {
471
479
  p--;
472
480
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
473
481
  }
474
482
  break;
475
- // line 476 "Parser.java"
483
+ // line 484 "Parser.java"
476
484
  }
477
485
  }
478
486
 
@@ -535,25 +543,25 @@ case 1:
535
543
  switch ( _JSON_value_actions[_acts++] )
536
544
  {
537
545
  case 0:
538
- // line 341 "Parser.rl"
546
+ // line 349 "Parser.rl"
539
547
  {
540
548
  result = getRuntime().getNil();
541
549
  }
542
550
  break;
543
551
  case 1:
544
- // line 344 "Parser.rl"
552
+ // line 352 "Parser.rl"
545
553
  {
546
554
  result = getRuntime().getFalse();
547
555
  }
548
556
  break;
549
557
  case 2:
550
- // line 347 "Parser.rl"
558
+ // line 355 "Parser.rl"
551
559
  {
552
560
  result = getRuntime().getTrue();
553
561
  }
554
562
  break;
555
563
  case 3:
556
- // line 350 "Parser.rl"
564
+ // line 358 "Parser.rl"
557
565
  {
558
566
  if (parser.allowNaN) {
559
567
  result = getConstant(CONST_NAN);
@@ -563,7 +571,7 @@ case 1:
563
571
  }
564
572
  break;
565
573
  case 4:
566
- // line 357 "Parser.rl"
574
+ // line 365 "Parser.rl"
567
575
  {
568
576
  if (parser.allowNaN) {
569
577
  result = getConstant(CONST_INFINITY);
@@ -573,7 +581,7 @@ case 1:
573
581
  }
574
582
  break;
575
583
  case 5:
576
- // line 364 "Parser.rl"
584
+ // line 372 "Parser.rl"
577
585
  {
578
586
  if (pe > p + 8 &&
579
587
  absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) {
@@ -602,7 +610,7 @@ case 1:
602
610
  }
603
611
  break;
604
612
  case 6:
605
- // line 390 "Parser.rl"
613
+ // line 398 "Parser.rl"
606
614
  {
607
615
  parseString(res, p, pe);
608
616
  if (res.result == null) {
@@ -615,7 +623,7 @@ case 1:
615
623
  }
616
624
  break;
617
625
  case 7:
618
- // line 400 "Parser.rl"
626
+ // line 408 "Parser.rl"
619
627
  {
620
628
  currentNesting++;
621
629
  parseArray(res, p, pe);
@@ -630,7 +638,7 @@ case 1:
630
638
  }
631
639
  break;
632
640
  case 8:
633
- // line 412 "Parser.rl"
641
+ // line 420 "Parser.rl"
634
642
  {
635
643
  currentNesting++;
636
644
  parseObject(res, p, pe);
@@ -644,7 +652,7 @@ case 1:
644
652
  }
645
653
  }
646
654
  break;
647
- // line 648 "Parser.java"
655
+ // line 656 "Parser.java"
648
656
  }
649
657
  }
650
658
  }
@@ -664,9 +672,12 @@ case 5:
664
672
  break; }
665
673
  }
666
674
 
667
- // line 447 "Parser.rl"
675
+ // line 455 "Parser.rl"
668
676
 
669
677
  if (cs >= JSON_value_first_final && result != null) {
678
+ if (parser.freeze) {
679
+ result.setFrozen(true);
680
+ }
670
681
  res.update(result, p);
671
682
  } else {
672
683
  res.update(null, p);
@@ -674,7 +685,7 @@ case 5:
674
685
  }
675
686
 
676
687
 
677
- // line 678 "Parser.java"
688
+ // line 689 "Parser.java"
678
689
  private static byte[] init__JSON_integer_actions_0()
679
690
  {
680
691
  return new byte [] {
@@ -773,7 +784,7 @@ static final int JSON_integer_error = 0;
773
784
  static final int JSON_integer_en_main = 1;
774
785
 
775
786
 
776
- // line 466 "Parser.rl"
787
+ // line 477 "Parser.rl"
777
788
 
778
789
 
779
790
  void parseInteger(ParserResult res, int p, int pe) {
@@ -791,15 +802,15 @@ static final int JSON_integer_en_main = 1;
791
802
  int cs = EVIL;
792
803
 
793
804
 
794
- // line 795 "Parser.java"
805
+ // line 806 "Parser.java"
795
806
  {
796
807
  cs = JSON_integer_start;
797
808
  }
798
809
 
799
- // line 483 "Parser.rl"
810
+ // line 494 "Parser.rl"
800
811
  int memo = p;
801
812
 
802
- // line 803 "Parser.java"
813
+ // line 814 "Parser.java"
803
814
  {
804
815
  int _klen;
805
816
  int _trans = 0;
@@ -880,13 +891,13 @@ case 1:
880
891
  switch ( _JSON_integer_actions[_acts++] )
881
892
  {
882
893
  case 0:
883
- // line 460 "Parser.rl"
894
+ // line 471 "Parser.rl"
884
895
  {
885
896
  p--;
886
897
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
887
898
  }
888
899
  break;
889
- // line 890 "Parser.java"
900
+ // line 901 "Parser.java"
890
901
  }
891
902
  }
892
903
  }
@@ -906,7 +917,7 @@ case 5:
906
917
  break; }
907
918
  }
908
919
 
909
- // line 485 "Parser.rl"
920
+ // line 496 "Parser.rl"
910
921
 
911
922
  if (cs < JSON_integer_first_final) {
912
923
  return -1;
@@ -914,13 +925,13 @@ case 5:
914
925
 
915
926
  return p;
916
927
  }
917
-
928
+
918
929
  RubyInteger createInteger(int p, int new_p) {
919
930
  Ruby runtime = getRuntime();
920
931
  ByteList num = absSubSequence(p, new_p);
921
932
  return bytesToInum(runtime, num);
922
933
  }
923
-
934
+
924
935
  RubyInteger bytesToInum(Ruby runtime, ByteList num) {
925
936
  return runtime.is1_9() ?
926
937
  ConvertBytes.byteListToInum19(runtime, num, 10, true) :
@@ -928,7 +939,7 @@ case 5:
928
939
  }
929
940
 
930
941
 
931
- // line 932 "Parser.java"
942
+ // line 943 "Parser.java"
932
943
  private static byte[] init__JSON_float_actions_0()
933
944
  {
934
945
  return new byte [] {
@@ -1030,7 +1041,7 @@ static final int JSON_float_error = 0;
1030
1041
  static final int JSON_float_en_main = 1;
1031
1042
 
1032
1043
 
1033
- // line 520 "Parser.rl"
1044
+ // line 531 "Parser.rl"
1034
1045
 
1035
1046
 
1036
1047
  void parseFloat(ParserResult res, int p, int pe) {
@@ -1039,7 +1050,9 @@ static final int JSON_float_en_main = 1;
1039
1050
  res.update(null, p);
1040
1051
  return;
1041
1052
  }
1042
- RubyFloat number = createFloat(p, new_p);
1053
+ IRubyObject number = parser.decimalClass == null ?
1054
+ createFloat(p, new_p) : createCustomDecimal(p, new_p);
1055
+
1043
1056
  res.update(number, new_p + 1);
1044
1057
  return;
1045
1058
  }
@@ -1048,15 +1061,15 @@ static final int JSON_float_en_main = 1;
1048
1061
  int cs = EVIL;
1049
1062
 
1050
1063
 
1051
- // line 1052 "Parser.java"
1064
+ // line 1065 "Parser.java"
1052
1065
  {
1053
1066
  cs = JSON_float_start;
1054
1067
  }
1055
1068
 
1056
- // line 537 "Parser.rl"
1069
+ // line 550 "Parser.rl"
1057
1070
  int memo = p;
1058
1071
 
1059
- // line 1060 "Parser.java"
1072
+ // line 1073 "Parser.java"
1060
1073
  {
1061
1074
  int _klen;
1062
1075
  int _trans = 0;
@@ -1137,13 +1150,13 @@ case 1:
1137
1150
  switch ( _JSON_float_actions[_acts++] )
1138
1151
  {
1139
1152
  case 0:
1140
- // line 511 "Parser.rl"
1153
+ // line 522 "Parser.rl"
1141
1154
  {
1142
1155
  p--;
1143
1156
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1144
1157
  }
1145
1158
  break;
1146
- // line 1147 "Parser.java"
1159
+ // line 1160 "Parser.java"
1147
1160
  }
1148
1161
  }
1149
1162
  }
@@ -1163,23 +1176,30 @@ case 5:
1163
1176
  break; }
1164
1177
  }
1165
1178
 
1166
- // line 539 "Parser.rl"
1179
+ // line 552 "Parser.rl"
1167
1180
 
1168
1181
  if (cs < JSON_float_first_final) {
1169
1182
  return -1;
1170
1183
  }
1171
-
1184
+
1172
1185
  return p;
1173
1186
  }
1174
-
1187
+
1175
1188
  RubyFloat createFloat(int p, int new_p) {
1176
1189
  Ruby runtime = getRuntime();
1177
1190
  ByteList num = absSubSequence(p, new_p);
1178
1191
  return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
1179
1192
  }
1180
1193
 
1194
+ IRubyObject createCustomDecimal(int p, int new_p) {
1195
+ Ruby runtime = getRuntime();
1196
+ ByteList num = absSubSequence(p, new_p);
1197
+ IRubyObject numString = runtime.newString(num.toString());
1198
+ return parser.decimalClass.callMethod(context, "new", numString);
1199
+ }
1200
+
1181
1201
 
1182
- // line 1183 "Parser.java"
1202
+ // line 1203 "Parser.java"
1183
1203
  private static byte[] init__JSON_string_actions_0()
1184
1204
  {
1185
1205
  return new byte [] {
@@ -1281,7 +1301,7 @@ static final int JSON_string_error = 0;
1281
1301
  static final int JSON_string_en_main = 1;
1282
1302
 
1283
1303
 
1284
- // line 584 "Parser.rl"
1304
+ // line 604 "Parser.rl"
1285
1305
 
1286
1306
 
1287
1307
  void parseString(ParserResult res, int p, int pe) {
@@ -1289,15 +1309,15 @@ static final int JSON_string_en_main = 1;
1289
1309
  IRubyObject result = null;
1290
1310
 
1291
1311
 
1292
- // line 1293 "Parser.java"
1312
+ // line 1313 "Parser.java"
1293
1313
  {
1294
1314
  cs = JSON_string_start;
1295
1315
  }
1296
1316
 
1297
- // line 591 "Parser.rl"
1317
+ // line 611 "Parser.rl"
1298
1318
  int memo = p;
1299
1319
 
1300
- // line 1301 "Parser.java"
1320
+ // line 1321 "Parser.java"
1301
1321
  {
1302
1322
  int _klen;
1303
1323
  int _trans = 0;
@@ -1378,7 +1398,7 @@ case 1:
1378
1398
  switch ( _JSON_string_actions[_acts++] )
1379
1399
  {
1380
1400
  case 0:
1381
- // line 559 "Parser.rl"
1401
+ // line 579 "Parser.rl"
1382
1402
  {
1383
1403
  int offset = byteList.begin();
1384
1404
  ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1393,13 +1413,13 @@ case 1:
1393
1413
  }
1394
1414
  break;
1395
1415
  case 1:
1396
- // line 572 "Parser.rl"
1416
+ // line 592 "Parser.rl"
1397
1417
  {
1398
1418
  p--;
1399
1419
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1400
1420
  }
1401
1421
  break;
1402
- // line 1403 "Parser.java"
1422
+ // line 1423 "Parser.java"
1403
1423
  }
1404
1424
  }
1405
1425
  }
@@ -1419,10 +1439,10 @@ case 5:
1419
1439
  break; }
1420
1440
  }
1421
1441
 
1422
- // line 593 "Parser.rl"
1442
+ // line 613 "Parser.rl"
1423
1443
 
1424
1444
  if (parser.createAdditions) {
1425
- RubyHash matchString = parser.matchString;
1445
+ RubyHash matchString = parser.match_string;
1426
1446
  if (matchString != null) {
1427
1447
  final IRubyObject[] memoArray = { result, null };
1428
1448
  try {
@@ -1446,18 +1466,25 @@ case 5:
1446
1466
  }
1447
1467
  }
1448
1468
 
1449
- if (cs >= JSON_string_first_final && result != null) {
1469
+ if (cs >= JSON_string_first_final && result != null) {
1450
1470
  if (result instanceof RubyString) {
1451
- ((RubyString)result).force_encoding(context, info.utf8.get());
1471
+ RubyString string = (RubyString)result;
1472
+ string.force_encoding(context, info.utf8.get());
1473
+ if (parser.freeze) {
1474
+ string.setFrozen(true);
1475
+ string = getRuntime().freezeAndDedupString(string);
1476
+ }
1477
+ res.update(string, p + 1);
1478
+ } else {
1479
+ res.update(result, p + 1);
1452
1480
  }
1453
- res.update(result, p + 1);
1454
1481
  } else {
1455
1482
  res.update(null, p + 1);
1456
1483
  }
1457
1484
  }
1458
1485
 
1459
1486
 
1460
- // line 1461 "Parser.java"
1487
+ // line 1488 "Parser.java"
1461
1488
  private static byte[] init__JSON_array_actions_0()
1462
1489
  {
1463
1490
  return new byte [] {
@@ -1570,7 +1597,7 @@ static final int JSON_array_error = 0;
1570
1597
  static final int JSON_array_en_main = 1;
1571
1598
 
1572
1599
 
1573
- // line 666 "Parser.rl"
1600
+ // line 693 "Parser.rl"
1574
1601
 
1575
1602
 
1576
1603
  void parseArray(ParserResult res, int p, int pe) {
@@ -1590,14 +1617,14 @@ static final int JSON_array_en_main = 1;
1590
1617
  }
1591
1618
 
1592
1619
 
1593
- // line 1594 "Parser.java"
1620
+ // line 1621 "Parser.java"
1594
1621
  {
1595
1622
  cs = JSON_array_start;
1596
1623
  }
1597
1624
 
1598
- // line 685 "Parser.rl"
1625
+ // line 712 "Parser.rl"
1599
1626
 
1600
- // line 1601 "Parser.java"
1627
+ // line 1628 "Parser.java"
1601
1628
  {
1602
1629
  int _klen;
1603
1630
  int _trans = 0;
@@ -1678,7 +1705,7 @@ case 1:
1678
1705
  switch ( _JSON_array_actions[_acts++] )
1679
1706
  {
1680
1707
  case 0:
1681
- // line 635 "Parser.rl"
1708
+ // line 662 "Parser.rl"
1682
1709
  {
1683
1710
  parseValue(res, p, pe);
1684
1711
  if (res.result == null) {
@@ -1695,13 +1722,13 @@ case 1:
1695
1722
  }
1696
1723
  break;
1697
1724
  case 1:
1698
- // line 650 "Parser.rl"
1725
+ // line 677 "Parser.rl"
1699
1726
  {
1700
1727
  p--;
1701
1728
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
1702
1729
  }
1703
1730
  break;
1704
- // line 1705 "Parser.java"
1731
+ // line 1732 "Parser.java"
1705
1732
  }
1706
1733
  }
1707
1734
  }
@@ -1721,7 +1748,7 @@ case 5:
1721
1748
  break; }
1722
1749
  }
1723
1750
 
1724
- // line 686 "Parser.rl"
1751
+ // line 713 "Parser.rl"
1725
1752
 
1726
1753
  if (cs >= JSON_array_first_final) {
1727
1754
  res.update(result, p + 1);
@@ -1731,7 +1758,7 @@ case 5:
1731
1758
  }
1732
1759
 
1733
1760
 
1734
- // line 1735 "Parser.java"
1761
+ // line 1762 "Parser.java"
1735
1762
  private static byte[] init__JSON_object_actions_0()
1736
1763
  {
1737
1764
  return new byte [] {
@@ -1854,7 +1881,7 @@ static final int JSON_object_error = 0;
1854
1881
  static final int JSON_object_en_main = 1;
1855
1882
 
1856
1883
 
1857
- // line 745 "Parser.rl"
1884
+ // line 772 "Parser.rl"
1858
1885
 
1859
1886
 
1860
1887
  void parseObject(ParserResult res, int p, int pe) {
@@ -1879,14 +1906,14 @@ static final int JSON_object_en_main = 1;
1879
1906
  }
1880
1907
 
1881
1908
 
1882
- // line 1883 "Parser.java"
1909
+ // line 1910 "Parser.java"
1883
1910
  {
1884
1911
  cs = JSON_object_start;
1885
1912
  }
1886
1913
 
1887
- // line 769 "Parser.rl"
1914
+ // line 796 "Parser.rl"
1888
1915
 
1889
- // line 1890 "Parser.java"
1916
+ // line 1917 "Parser.java"
1890
1917
  {
1891
1918
  int _klen;
1892
1919
  int _trans = 0;
@@ -1967,7 +1994,7 @@ case 1:
1967
1994
  switch ( _JSON_object_actions[_acts++] )
1968
1995
  {
1969
1996
  case 0:
1970
- // line 700 "Parser.rl"
1997
+ // line 727 "Parser.rl"
1971
1998
  {
1972
1999
  parseValue(res, p, pe);
1973
2000
  if (res.result == null) {
@@ -1984,7 +2011,7 @@ case 1:
1984
2011
  }
1985
2012
  break;
1986
2013
  case 1:
1987
- // line 715 "Parser.rl"
2014
+ // line 742 "Parser.rl"
1988
2015
  {
1989
2016
  parseString(res, p, pe);
1990
2017
  if (res.result == null) {
@@ -2004,13 +2031,13 @@ case 1:
2004
2031
  }
2005
2032
  break;
2006
2033
  case 2:
2007
- // line 733 "Parser.rl"
2034
+ // line 760 "Parser.rl"
2008
2035
  {
2009
2036
  p--;
2010
2037
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
2011
2038
  }
2012
2039
  break;
2013
- // line 2014 "Parser.java"
2040
+ // line 2041 "Parser.java"
2014
2041
  }
2015
2042
  }
2016
2043
  }
@@ -2030,7 +2057,7 @@ case 5:
2030
2057
  break; }
2031
2058
  }
2032
2059
 
2033
- // line 770 "Parser.rl"
2060
+ // line 797 "Parser.rl"
2034
2061
 
2035
2062
  if (cs < JSON_object_first_final) {
2036
2063
  res.update(null, p + 1);
@@ -2063,7 +2090,7 @@ case 5:
2063
2090
  }
2064
2091
 
2065
2092
 
2066
- // line 2067 "Parser.java"
2093
+ // line 2094 "Parser.java"
2067
2094
  private static byte[] init__JSON_actions_0()
2068
2095
  {
2069
2096
  return new byte [] {
@@ -2166,7 +2193,7 @@ static final int JSON_error = 0;
2166
2193
  static final int JSON_en_main = 1;
2167
2194
 
2168
2195
 
2169
- // line 821 "Parser.rl"
2196
+ // line 848 "Parser.rl"
2170
2197
 
2171
2198
 
2172
2199
  public IRubyObject parseImplemetation() {
@@ -2176,16 +2203,16 @@ static final int JSON_en_main = 1;
2176
2203
  ParserResult res = new ParserResult();
2177
2204
 
2178
2205
 
2179
- // line 2180 "Parser.java"
2206
+ // line 2207 "Parser.java"
2180
2207
  {
2181
2208
  cs = JSON_start;
2182
2209
  }
2183
2210
 
2184
- // line 830 "Parser.rl"
2211
+ // line 857 "Parser.rl"
2185
2212
  p = byteList.begin();
2186
2213
  pe = p + byteList.length();
2187
2214
 
2188
- // line 2189 "Parser.java"
2215
+ // line 2216 "Parser.java"
2189
2216
  {
2190
2217
  int _klen;
2191
2218
  int _trans = 0;
@@ -2266,7 +2293,7 @@ case 1:
2266
2293
  switch ( _JSON_actions[_acts++] )
2267
2294
  {
2268
2295
  case 0:
2269
- // line 807 "Parser.rl"
2296
+ // line 834 "Parser.rl"
2270
2297
  {
2271
2298
  parseValue(res, p, pe);
2272
2299
  if (res.result == null) {
@@ -2278,7 +2305,7 @@ case 1:
2278
2305
  }
2279
2306
  }
2280
2307
  break;
2281
- // line 2282 "Parser.java"
2308
+ // line 2309 "Parser.java"
2282
2309
  }
2283
2310
  }
2284
2311
  }
@@ -2298,7 +2325,7 @@ case 5:
2298
2325
  break; }
2299
2326
  }
2300
2327
 
2301
- // line 833 "Parser.rl"
2328
+ // line 860 "Parser.rl"
2302
2329
 
2303
2330
  if (cs >= JSON_first_final && p == pe) {
2304
2331
  return result;