json 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of json might be problematic. Click here for more details.

Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +23 -0
  4. data/CHANGES.md +391 -0
  5. data/Gemfile +14 -0
  6. data/README-json-jruby.md +33 -0
  7. data/README.md +409 -0
  8. data/Rakefile +408 -0
  9. data/VERSION +1 -0
  10. data/diagrams/.keep +0 -0
  11. data/ext/json/ext/fbuffer/fbuffer.h +187 -0
  12. data/ext/json/ext/generator/depend +1 -0
  13. data/ext/json/ext/generator/extconf.rb +4 -0
  14. data/ext/json/ext/generator/generator.c +1444 -0
  15. data/ext/json/ext/generator/generator.h +171 -0
  16. data/ext/json/ext/parser/depend +1 -0
  17. data/ext/json/ext/parser/extconf.rb +6 -0
  18. data/ext/json/ext/parser/parser.c +2131 -0
  19. data/ext/json/ext/parser/parser.h +91 -0
  20. data/ext/json/ext/parser/parser.rl +891 -0
  21. data/ext/json/extconf.rb +2 -0
  22. data/install.rb +23 -0
  23. data/java/src/json/ext/ByteListTranscoder.java +166 -0
  24. data/java/src/json/ext/Generator.java +443 -0
  25. data/java/src/json/ext/GeneratorMethods.java +231 -0
  26. data/java/src/json/ext/GeneratorService.java +42 -0
  27. data/java/src/json/ext/GeneratorState.java +490 -0
  28. data/java/src/json/ext/OptionsReader.java +113 -0
  29. data/java/src/json/ext/Parser.java +2362 -0
  30. data/java/src/json/ext/Parser.rl +893 -0
  31. data/java/src/json/ext/ParserService.java +34 -0
  32. data/java/src/json/ext/RuntimeInfo.java +116 -0
  33. data/java/src/json/ext/StringDecoder.java +166 -0
  34. data/java/src/json/ext/StringEncoder.java +111 -0
  35. data/java/src/json/ext/Utils.java +88 -0
  36. data/json-java.gemspec +38 -0
  37. data/json.gemspec +0 -0
  38. data/json_pure.gemspec +38 -0
  39. data/lib/json.rb +63 -0
  40. data/lib/json/add/bigdecimal.rb +29 -0
  41. data/lib/json/add/complex.rb +29 -0
  42. data/lib/json/add/core.rb +12 -0
  43. data/lib/json/add/date.rb +34 -0
  44. data/lib/json/add/date_time.rb +50 -0
  45. data/lib/json/add/exception.rb +31 -0
  46. data/lib/json/add/ostruct.rb +31 -0
  47. data/lib/json/add/range.rb +29 -0
  48. data/lib/json/add/rational.rb +28 -0
  49. data/lib/json/add/regexp.rb +30 -0
  50. data/lib/json/add/set.rb +29 -0
  51. data/lib/json/add/struct.rb +30 -0
  52. data/lib/json/add/symbol.rb +25 -0
  53. data/lib/json/add/time.rb +38 -0
  54. data/lib/json/common.rb +456 -0
  55. data/lib/json/ext.rb +15 -0
  56. data/lib/json/ext/.keep +0 -0
  57. data/lib/json/generic_object.rb +71 -0
  58. data/lib/json/pure.rb +15 -0
  59. data/lib/json/pure/generator.rb +458 -0
  60. data/lib/json/pure/parser.rb +311 -0
  61. data/lib/json/version.rb +9 -0
  62. data/references/rfc7159.txt +899 -0
  63. data/tests/fixtures/fail10.json +1 -0
  64. data/tests/fixtures/fail11.json +1 -0
  65. data/tests/fixtures/fail12.json +1 -0
  66. data/tests/fixtures/fail13.json +1 -0
  67. data/tests/fixtures/fail14.json +1 -0
  68. data/tests/fixtures/fail18.json +1 -0
  69. data/tests/fixtures/fail19.json +1 -0
  70. data/tests/fixtures/fail2.json +1 -0
  71. data/tests/fixtures/fail20.json +1 -0
  72. data/tests/fixtures/fail21.json +1 -0
  73. data/tests/fixtures/fail22.json +1 -0
  74. data/tests/fixtures/fail23.json +1 -0
  75. data/tests/fixtures/fail24.json +1 -0
  76. data/tests/fixtures/fail25.json +1 -0
  77. data/tests/fixtures/fail27.json +2 -0
  78. data/tests/fixtures/fail28.json +2 -0
  79. data/tests/fixtures/fail3.json +1 -0
  80. data/tests/fixtures/fail4.json +1 -0
  81. data/tests/fixtures/fail5.json +1 -0
  82. data/tests/fixtures/fail6.json +1 -0
  83. data/tests/fixtures/fail7.json +1 -0
  84. data/tests/fixtures/fail8.json +1 -0
  85. data/tests/fixtures/fail9.json +1 -0
  86. data/tests/fixtures/obsolete_fail1.json +1 -0
  87. data/tests/fixtures/pass1.json +56 -0
  88. data/tests/fixtures/pass15.json +1 -0
  89. data/tests/fixtures/pass16.json +1 -0
  90. data/tests/fixtures/pass17.json +1 -0
  91. data/tests/fixtures/pass2.json +1 -0
  92. data/tests/fixtures/pass26.json +1 -0
  93. data/tests/fixtures/pass3.json +6 -0
  94. data/tests/json_addition_test.rb +203 -0
  95. data/tests/json_common_interface_test.rb +126 -0
  96. data/tests/json_encoding_test.rb +107 -0
  97. data/tests/json_ext_parser_test.rb +15 -0
  98. data/tests/json_fixtures_test.rb +32 -0
  99. data/tests/json_generator_test.rb +377 -0
  100. data/tests/json_generic_object_test.rb +82 -0
  101. data/tests/json_parser_test.rb +472 -0
  102. data/tests/json_string_matching_test.rb +38 -0
  103. data/tests/test_helper.rb +17 -0
  104. data/tools/diff.sh +18 -0
  105. data/tools/fuzz.rb +131 -0
  106. data/tools/server.rb +62 -0
  107. metadata +185 -0
@@ -0,0 +1,893 @@
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
+ */
6
+ package json.ext;
7
+
8
+ import org.jruby.Ruby;
9
+ import org.jruby.RubyArray;
10
+ import org.jruby.RubyClass;
11
+ import org.jruby.RubyEncoding;
12
+ import org.jruby.RubyFloat;
13
+ import org.jruby.RubyHash;
14
+ import org.jruby.RubyInteger;
15
+ import org.jruby.RubyModule;
16
+ import org.jruby.RubyNumeric;
17
+ import org.jruby.RubyObject;
18
+ import org.jruby.RubyString;
19
+ import org.jruby.anno.JRubyMethod;
20
+ import org.jruby.exceptions.JumpException;
21
+ import org.jruby.exceptions.RaiseException;
22
+ import org.jruby.runtime.Block;
23
+ import org.jruby.runtime.ObjectAllocator;
24
+ import org.jruby.runtime.ThreadContext;
25
+ import org.jruby.runtime.Visibility;
26
+ import org.jruby.runtime.builtin.IRubyObject;
27
+ import org.jruby.util.ByteList;
28
+ import org.jruby.util.ConvertBytes;
29
+ import static org.jruby.util.ConvertDouble.DoubleConverter;
30
+
31
+ /**
32
+ * The <code>JSON::Ext::Parser</code> class.
33
+ *
34
+ * <p>This is the JSON parser implemented as a Java class. To use it as the
35
+ * standard parser, set
36
+ * <pre>JSON.parser = JSON::Ext::Parser</pre>
37
+ * This is performed for you when you <code>include "json/ext"</code>.
38
+ *
39
+ * <p>This class does not perform the actual parsing, just acts as an interface
40
+ * to Ruby code. When the {@link #parse()} method is invoked, a
41
+ * Parser.ParserSession object is instantiated, which handles the process.
42
+ *
43
+ * @author mernen
44
+ */
45
+ public class Parser extends RubyObject {
46
+ private final RuntimeInfo info;
47
+ private RubyString vSource;
48
+ private RubyString createId;
49
+ private boolean createAdditions;
50
+ private int maxNesting;
51
+ private boolean allowNaN;
52
+ private boolean symbolizeNames;
53
+ private RubyClass objectClass;
54
+ private RubyClass arrayClass;
55
+ private RubyClass decimalClass;
56
+ private RubyHash match_string;
57
+
58
+ private static final int DEFAULT_MAX_NESTING = 100;
59
+
60
+ private static final ByteList JSON_MINUS_INFINITY = new ByteList(ByteList.plain("-Infinity"));
61
+ // constant names in the JSON module containing those values
62
+ private static final String CONST_NAN = "NaN";
63
+ private static final String CONST_INFINITY = "Infinity";
64
+ private static final String CONST_MINUS_INFINITY = "MinusInfinity";
65
+
66
+ static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
67
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
68
+ return new Parser(runtime, klazz);
69
+ }
70
+ };
71
+
72
+ /**
73
+ * Multiple-value return for internal parser methods.
74
+ *
75
+ * <p>All the <code>parse<var>Stuff</var></code> methods return instances of
76
+ * <code>ParserResult</code> when successful, or <code>null</code> when
77
+ * there's a problem with the input data.
78
+ */
79
+ static final class ParserResult {
80
+ /**
81
+ * The result of the successful parsing. Should never be
82
+ * <code>null</code>.
83
+ */
84
+ IRubyObject result;
85
+ /**
86
+ * The point where the parser returned.
87
+ */
88
+ int p;
89
+
90
+ void update(IRubyObject result, int p) {
91
+ this.result = result;
92
+ this.p = p;
93
+ }
94
+ }
95
+
96
+ public Parser(Ruby runtime, RubyClass metaClass) {
97
+ super(runtime, metaClass);
98
+ info = RuntimeInfo.forRuntime(runtime);
99
+ }
100
+
101
+ /**
102
+ * <code>Parser.new(source, opts = {})</code>
103
+ *
104
+ * <p>Creates a new <code>JSON::Ext::Parser</code> instance for the string
105
+ * <code>source</code>.
106
+ * It will be configured by the <code>opts</code> Hash.
107
+ * <code>opts</code> can have the following keys:
108
+ *
109
+ * <dl>
110
+ * <dt><code>:max_nesting</code>
111
+ * <dd>The maximum depth of nesting allowed in the parsed data
112
+ * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>,
113
+ * it defaults to 100.
114
+ *
115
+ * <dt><code>:allow_nan</code>
116
+ * <dd>If set to <code>true</code>, allow <code>NaN</code>,
117
+ * <code>Infinity</code> and <code>-Infinity</code> in defiance of RFC 4627
118
+ * to be parsed by the Parser. This option defaults to <code>false</code>.
119
+ *
120
+ * <dt><code>:symbolize_names</code>
121
+ * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
122
+ * a JSON object. Otherwise strings are returned, which is also the default.
123
+ *
124
+ * <dt><code>:create_additions</code>
125
+ * <dd>If set to <code>false</code>, the Parser doesn't create additions
126
+ * even if a matching class and <code>create_id</code> was found. This option
127
+ * defaults to <code>true</code>.
128
+ *
129
+ * <dt><code>:object_class</code>
130
+ * <dd>Defaults to Hash.
131
+ *
132
+ * <dt><code>:array_class</code>
133
+ * <dd>Defaults to Array.
134
+ *
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.
139
+ * </dl>
140
+ */
141
+ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
142
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) {
143
+ Parser parser = (Parser)((RubyClass)clazz).allocate();
144
+
145
+ parser.callInit(args, block);
146
+
147
+ return parser;
148
+ }
149
+
150
+ @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
151
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
152
+ Ruby runtime = context.getRuntime();
153
+ if (this.vSource != null) {
154
+ throw runtime.newTypeError("already initialized instance");
155
+ }
156
+
157
+ OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
158
+ this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
159
+ this.allowNaN = opts.getBool("allow_nan", false);
160
+ this.symbolizeNames = opts.getBool("symbolize_names", false);
161
+ this.createId = opts.getString("create_id", getCreateId(context));
162
+ this.createAdditions = opts.getBool("create_additions", false);
163
+ this.objectClass = opts.getClass("object_class", runtime.getHash());
164
+ this.arrayClass = opts.getClass("array_class", runtime.getArray());
165
+ this.decimalClass = opts.getClass("decimal_class", null);
166
+ this.match_string = opts.getHash("match_string");
167
+
168
+ if(symbolizeNames && createAdditions) {
169
+ throw runtime.newArgumentError(
170
+ "options :symbolize_names and :create_additions cannot be " +
171
+ " used in conjunction"
172
+ );
173
+ }
174
+ this.vSource = args[0].convertToString();
175
+ this.vSource = convertEncoding(context, vSource);
176
+
177
+ return this;
178
+ }
179
+
180
+ /**
181
+ * Checks the given string's encoding. If a non-UTF-8 encoding is detected,
182
+ * a converted copy is returned.
183
+ * Returns the source string if no conversion is needed.
184
+ */
185
+ private RubyString convertEncoding(ThreadContext context, RubyString 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;
196
+ }
197
+
198
+ /**
199
+ * Checks the first four bytes of the given ByteList to infer its encoding,
200
+ * using the principle demonstrated on section 3 of RFC 4627 (JSON).
201
+ */
202
+ private static String sniffByteList(ByteList bl) {
203
+ if (bl.length() < 4) return null;
204
+ if (bl.get(0) == 0 && bl.get(2) == 0) {
205
+ return bl.get(1) == 0 ? "utf-32be" : "utf-16be";
206
+ }
207
+ if (bl.get(1) == 0 && bl.get(3) == 0) {
208
+ return bl.get(2) == 0 ? "utf-32le" : "utf-16le";
209
+ }
210
+ return null;
211
+ }
212
+
213
+ /**
214
+ * Assumes the given (binary) RubyString to be in the given encoding, then
215
+ * converts it to UTF-8.
216
+ */
217
+ private RubyString reinterpretEncoding(ThreadContext context,
218
+ RubyString str, String sniffedEncoding) {
219
+ RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
220
+ RubyEncoding targetEncoding = info.utf8.get();
221
+ RubyString dup = (RubyString)str.dup();
222
+ dup.force_encoding(context, actualEncoding);
223
+ return (RubyString)dup.encode_bang(context, targetEncoding);
224
+ }
225
+
226
+ /**
227
+ * <code>Parser#parse()</code>
228
+ *
229
+ * <p>Parses the current JSON text <code>source</code> and returns the
230
+ * complete data structure as a result.
231
+ */
232
+ @JRubyMethod
233
+ public IRubyObject parse(ThreadContext context) {
234
+ return new ParserSession(this, context, info).parse();
235
+ }
236
+
237
+ /**
238
+ * <code>Parser#source()</code>
239
+ *
240
+ * <p>Returns a copy of the current <code>source</code> string, that was
241
+ * used to construct this Parser.
242
+ */
243
+ @JRubyMethod(name = "source")
244
+ public IRubyObject source_get() {
245
+ return checkAndGetSource().dup();
246
+ }
247
+
248
+ public RubyString checkAndGetSource() {
249
+ if (vSource != null) {
250
+ return vSource;
251
+ } else {
252
+ throw getRuntime().newTypeError("uninitialized instance");
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Queries <code>JSON.create_id</code>. Returns <code>null</code> if it is
258
+ * set to <code>nil</code> or <code>false</code>, and a String if not.
259
+ */
260
+ private RubyString getCreateId(ThreadContext context) {
261
+ IRubyObject v = info.jsonModule.get().callMethod(context, "create_id");
262
+ return v.isTrue() ? v.convertToString() : null;
263
+ }
264
+
265
+ /**
266
+ * A string parsing session.
267
+ *
268
+ * <p>Once a ParserSession is instantiated, the source string should not
269
+ * change until the parsing is complete. The ParserSession object assumes
270
+ * the source {@link RubyString} is still associated to its original
271
+ * {@link ByteList}, which in turn must still be bound to the same
272
+ * <code>byte[]</code> value (and on the same offset).
273
+ */
274
+ // Ragel uses lots of fall-through
275
+ @SuppressWarnings("fallthrough")
276
+ private static class ParserSession {
277
+ private final Parser parser;
278
+ private final ThreadContext context;
279
+ private final RuntimeInfo info;
280
+ private final ByteList byteList;
281
+ private final ByteList view;
282
+ private final byte[] data;
283
+ private final StringDecoder decoder;
284
+ private int currentNesting = 0;
285
+ private final DoubleConverter dc;
286
+
287
+ // initialization value for all state variables.
288
+ // no idea about the origins of this value, ask Flori ;)
289
+ private static final int EVIL = 0x666;
290
+
291
+ private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) {
292
+ this.parser = parser;
293
+ this.context = context;
294
+ this.info = info;
295
+ this.byteList = parser.checkAndGetSource().getByteList();
296
+ this.data = byteList.unsafeBytes();
297
+ this.view = new ByteList(data, false);
298
+ this.decoder = new StringDecoder(context);
299
+ this.dc = new DoubleConverter();
300
+ }
301
+
302
+ private RaiseException unexpectedToken(int absStart, int absEnd) {
303
+ RubyString msg = getRuntime().newString("unexpected token at '")
304
+ .cat(data, absStart, absEnd - absStart)
305
+ .cat((byte)'\'');
306
+ return newException(Utils.M_PARSER_ERROR, msg);
307
+ }
308
+
309
+ private Ruby getRuntime() {
310
+ return context.getRuntime();
311
+ }
312
+
313
+ %%{
314
+ machine JSON_common;
315
+
316
+ cr = '\n';
317
+ cr_neg = [^\n];
318
+ ws = [ \t\r\n];
319
+ c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/';
320
+ cpp_comment = '//' cr_neg* cr;
321
+ comment = c_comment | cpp_comment;
322
+ ignore = ws | comment;
323
+ name_separator = ':';
324
+ value_separator = ',';
325
+ Vnull = 'null';
326
+ Vfalse = 'false';
327
+ Vtrue = 'true';
328
+ VNaN = 'NaN';
329
+ VInfinity = 'Infinity';
330
+ VMinusInfinity = '-Infinity';
331
+ begin_value = [nft"\-[{NI] | digit;
332
+ begin_object = '{';
333
+ end_object = '}';
334
+ begin_array = '[';
335
+ end_array = ']';
336
+ begin_string = '"';
337
+ begin_name = begin_string;
338
+ begin_number = digit | '-';
339
+ }%%
340
+
341
+ %%{
342
+ machine JSON_value;
343
+ include JSON_common;
344
+
345
+ write data;
346
+
347
+ action parse_null {
348
+ result = getRuntime().getNil();
349
+ }
350
+ action parse_false {
351
+ result = getRuntime().getFalse();
352
+ }
353
+ action parse_true {
354
+ result = getRuntime().getTrue();
355
+ }
356
+ action parse_nan {
357
+ if (parser.allowNaN) {
358
+ result = getConstant(CONST_NAN);
359
+ } else {
360
+ throw unexpectedToken(p - 2, pe);
361
+ }
362
+ }
363
+ action parse_infinity {
364
+ if (parser.allowNaN) {
365
+ result = getConstant(CONST_INFINITY);
366
+ } else {
367
+ throw unexpectedToken(p - 7, pe);
368
+ }
369
+ }
370
+ action parse_number {
371
+ if (pe > fpc + 8 &&
372
+ absSubSequence(fpc, fpc + 9).equals(JSON_MINUS_INFINITY)) {
373
+
374
+ if (parser.allowNaN) {
375
+ result = getConstant(CONST_MINUS_INFINITY);
376
+ fexec p + 10;
377
+ fhold;
378
+ fbreak;
379
+ } else {
380
+ throw unexpectedToken(p, pe);
381
+ }
382
+ }
383
+ parseFloat(res, fpc, pe);
384
+ if (res.result != null) {
385
+ result = res.result;
386
+ fexec res.p;
387
+ }
388
+ parseInteger(res, fpc, pe);
389
+ if (res.result != null) {
390
+ result = res.result;
391
+ fexec res.p;
392
+ }
393
+ fhold;
394
+ fbreak;
395
+ }
396
+ action parse_string {
397
+ parseString(res, fpc, pe);
398
+ if (res.result == null) {
399
+ fhold;
400
+ fbreak;
401
+ } else {
402
+ result = res.result;
403
+ fexec res.p;
404
+ }
405
+ }
406
+ action parse_array {
407
+ currentNesting++;
408
+ parseArray(res, fpc, pe);
409
+ currentNesting--;
410
+ if (res.result == null) {
411
+ fhold;
412
+ fbreak;
413
+ } else {
414
+ result = res.result;
415
+ fexec res.p;
416
+ }
417
+ }
418
+ action parse_object {
419
+ currentNesting++;
420
+ parseObject(res, fpc, pe);
421
+ currentNesting--;
422
+ if (res.result == null) {
423
+ fhold;
424
+ fbreak;
425
+ } else {
426
+ result = res.result;
427
+ fexec res.p;
428
+ }
429
+ }
430
+ action exit {
431
+ fhold;
432
+ fbreak;
433
+ }
434
+
435
+ main := ( Vnull @parse_null |
436
+ Vfalse @parse_false |
437
+ Vtrue @parse_true |
438
+ VNaN @parse_nan |
439
+ VInfinity @parse_infinity |
440
+ begin_number >parse_number |
441
+ begin_string >parse_string |
442
+ begin_array >parse_array |
443
+ begin_object >parse_object
444
+ ) %*exit;
445
+ }%%
446
+
447
+ void parseValue(ParserResult res, int p, int pe) {
448
+ int cs = EVIL;
449
+ IRubyObject result = null;
450
+
451
+ %% write init;
452
+ %% write exec;
453
+
454
+ if (cs >= JSON_value_first_final && result != null) {
455
+ res.update(result, p);
456
+ } else {
457
+ res.update(null, p);
458
+ }
459
+ }
460
+
461
+ %%{
462
+ machine JSON_integer;
463
+
464
+ write data;
465
+
466
+ action exit {
467
+ fhold;
468
+ fbreak;
469
+ }
470
+
471
+ main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9]? @exit );
472
+ }%%
473
+
474
+ void parseInteger(ParserResult res, int p, int pe) {
475
+ int new_p = parseIntegerInternal(p, pe);
476
+ if (new_p == -1) {
477
+ res.update(null, p);
478
+ return;
479
+ }
480
+ RubyInteger number = createInteger(p, new_p);
481
+ res.update(number, new_p + 1);
482
+ return;
483
+ }
484
+
485
+ int parseIntegerInternal(int p, int pe) {
486
+ int cs = EVIL;
487
+
488
+ %% write init;
489
+ int memo = p;
490
+ %% write exec;
491
+
492
+ if (cs < JSON_integer_first_final) {
493
+ return -1;
494
+ }
495
+
496
+ return p;
497
+ }
498
+
499
+ RubyInteger createInteger(int p, int new_p) {
500
+ Ruby runtime = getRuntime();
501
+ ByteList num = absSubSequence(p, new_p);
502
+ return bytesToInum(runtime, num);
503
+ }
504
+
505
+ RubyInteger bytesToInum(Ruby runtime, ByteList num) {
506
+ return runtime.is1_9() ?
507
+ ConvertBytes.byteListToInum19(runtime, num, 10, true) :
508
+ ConvertBytes.byteListToInum(runtime, num, 10, true);
509
+ }
510
+
511
+ %%{
512
+ machine JSON_float;
513
+ include JSON_common;
514
+
515
+ write data;
516
+
517
+ action exit {
518
+ fhold;
519
+ fbreak;
520
+ }
521
+
522
+ main := '-'?
523
+ ( ( ( '0' | [1-9][0-9]* ) '.' [0-9]+ ( [Ee] [+\-]?[0-9]+ )? )
524
+ | ( ( '0' | [1-9][0-9]* ) ( [Ee] [+\-]? [0-9]+ ) ) )
525
+ ( ^[0-9Ee.\-]? @exit );
526
+ }%%
527
+
528
+ void parseFloat(ParserResult res, int p, int pe) {
529
+ int new_p = parseFloatInternal(p, pe);
530
+ if (new_p == -1) {
531
+ res.update(null, p);
532
+ return;
533
+ }
534
+ IRubyObject number = parser.decimalClass == null ?
535
+ createFloat(p, new_p) : createCustomDecimal(p, new_p);
536
+
537
+ res.update(number, new_p + 1);
538
+ return;
539
+ }
540
+
541
+ int parseFloatInternal(int p, int pe) {
542
+ int cs = EVIL;
543
+
544
+ %% write init;
545
+ int memo = p;
546
+ %% write exec;
547
+
548
+ if (cs < JSON_float_first_final) {
549
+ return -1;
550
+ }
551
+
552
+ return p;
553
+ }
554
+
555
+ RubyFloat createFloat(int p, int new_p) {
556
+ Ruby runtime = getRuntime();
557
+ ByteList num = absSubSequence(p, new_p);
558
+ return RubyFloat.newFloat(runtime, dc.parse(num, true, runtime.is1_9()));
559
+ }
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
+
568
+ %%{
569
+ machine JSON_string;
570
+ include JSON_common;
571
+
572
+ write data;
573
+
574
+ action parse_string {
575
+ int offset = byteList.begin();
576
+ ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
577
+ p - offset);
578
+ result = getRuntime().newString(decoded);
579
+ if (result == null) {
580
+ fhold;
581
+ fbreak;
582
+ } else {
583
+ fexec p + 1;
584
+ }
585
+ }
586
+
587
+ action exit {
588
+ fhold;
589
+ fbreak;
590
+ }
591
+
592
+ main := '"'
593
+ ( ( ^(["\\]|0..0x1f)
594
+ | '\\'["\\/bfnrt]
595
+ | '\\u'[0-9a-fA-F]{4}
596
+ | '\\'^(["\\/bfnrtu]|0..0x1f)
597
+ )* %parse_string
598
+ ) '"' @exit;
599
+ }%%
600
+
601
+ void parseString(ParserResult res, int p, int pe) {
602
+ int cs = EVIL;
603
+ IRubyObject result = null;
604
+
605
+ %% write init;
606
+ int memo = p;
607
+ %% write exec;
608
+
609
+ if (parser.createAdditions) {
610
+ RubyHash matchString = parser.match_string;
611
+ if (matchString != null) {
612
+ final IRubyObject[] memoArray = { result, null };
613
+ try {
614
+ matchString.visitAll(new RubyHash.Visitor() {
615
+ @Override
616
+ public void visit(IRubyObject pattern, IRubyObject klass) {
617
+ if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) {
618
+ memoArray[1] = klass;
619
+ throw JumpException.SPECIAL_JUMP;
620
+ }
621
+ }
622
+ });
623
+ } catch (JumpException e) { }
624
+ if (memoArray[1] != null) {
625
+ RubyClass klass = (RubyClass) memoArray[1];
626
+ if (klass.respondsTo("json_creatable?") &&
627
+ klass.callMethod(context, "json_creatable?").isTrue()) {
628
+ result = klass.callMethod(context, "json_create", result);
629
+ }
630
+ }
631
+ }
632
+ }
633
+
634
+ if (cs >= JSON_string_first_final && result != null) {
635
+ if (result instanceof RubyString) {
636
+ ((RubyString)result).force_encoding(context, info.utf8.get());
637
+ }
638
+ res.update(result, p + 1);
639
+ } else {
640
+ res.update(null, p + 1);
641
+ }
642
+ }
643
+
644
+ %%{
645
+ machine JSON_array;
646
+ include JSON_common;
647
+
648
+ write data;
649
+
650
+ action parse_value {
651
+ parseValue(res, fpc, pe);
652
+ if (res.result == null) {
653
+ fhold;
654
+ fbreak;
655
+ } else {
656
+ if (parser.arrayClass == getRuntime().getArray()) {
657
+ ((RubyArray)result).append(res.result);
658
+ } else {
659
+ result.callMethod(context, "<<", res.result);
660
+ }
661
+ fexec res.p;
662
+ }
663
+ }
664
+
665
+ action exit {
666
+ fhold;
667
+ fbreak;
668
+ }
669
+
670
+ next_element = value_separator ignore* begin_value >parse_value;
671
+
672
+ main := begin_array
673
+ ignore*
674
+ ( ( begin_value >parse_value
675
+ ignore* )
676
+ ( ignore*
677
+ next_element
678
+ ignore* )* )?
679
+ ignore*
680
+ end_array @exit;
681
+ }%%
682
+
683
+ void parseArray(ParserResult res, int p, int pe) {
684
+ int cs = EVIL;
685
+
686
+ if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
687
+ throw newException(Utils.M_NESTING_ERROR,
688
+ "nesting of " + currentNesting + " is too deep");
689
+ }
690
+
691
+ IRubyObject result;
692
+ if (parser.arrayClass == getRuntime().getArray()) {
693
+ result = RubyArray.newArray(getRuntime());
694
+ } else {
695
+ result = parser.arrayClass.newInstance(context,
696
+ IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
697
+ }
698
+
699
+ %% write init;
700
+ %% write exec;
701
+
702
+ if (cs >= JSON_array_first_final) {
703
+ res.update(result, p + 1);
704
+ } else {
705
+ throw unexpectedToken(p, pe);
706
+ }
707
+ }
708
+
709
+ %%{
710
+ machine JSON_object;
711
+ include JSON_common;
712
+
713
+ write data;
714
+
715
+ action parse_value {
716
+ parseValue(res, fpc, pe);
717
+ if (res.result == null) {
718
+ fhold;
719
+ fbreak;
720
+ } else {
721
+ if (parser.objectClass == getRuntime().getHash()) {
722
+ ((RubyHash)result).op_aset(context, lastName, res.result);
723
+ } else {
724
+ result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
725
+ }
726
+ fexec res.p;
727
+ }
728
+ }
729
+
730
+ action parse_name {
731
+ parseString(res, fpc, pe);
732
+ if (res.result == null) {
733
+ fhold;
734
+ fbreak;
735
+ } else {
736
+ RubyString name = (RubyString)res.result;
737
+ if (parser.symbolizeNames) {
738
+ lastName = context.getRuntime().is1_9()
739
+ ? name.intern19()
740
+ : name.intern();
741
+ } else {
742
+ lastName = name;
743
+ }
744
+ fexec res.p;
745
+ }
746
+ }
747
+
748
+ action exit {
749
+ fhold;
750
+ fbreak;
751
+ }
752
+
753
+ pair = ignore* begin_name >parse_name ignore* name_separator
754
+ ignore* begin_value >parse_value;
755
+ next_pair = ignore* value_separator pair;
756
+
757
+ main := (
758
+ begin_object (pair (next_pair)*)? ignore* end_object
759
+ ) @exit;
760
+ }%%
761
+
762
+ void parseObject(ParserResult res, int p, int pe) {
763
+ int cs = EVIL;
764
+ IRubyObject lastName = null;
765
+ boolean objectDefault = true;
766
+
767
+ if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) {
768
+ throw newException(Utils.M_NESTING_ERROR,
769
+ "nesting of " + currentNesting + " is too deep");
770
+ }
771
+
772
+ // this is guaranteed to be a RubyHash due to the earlier
773
+ // allocator test at OptionsReader#getClass
774
+ IRubyObject result;
775
+ if (parser.objectClass == getRuntime().getHash()) {
776
+ result = RubyHash.newHash(getRuntime());
777
+ } else {
778
+ objectDefault = false;
779
+ result = parser.objectClass.newInstance(context,
780
+ IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
781
+ }
782
+
783
+ %% write init;
784
+ %% write exec;
785
+
786
+ if (cs < JSON_object_first_final) {
787
+ res.update(null, p + 1);
788
+ return;
789
+ }
790
+
791
+ IRubyObject returnedResult = result;
792
+
793
+ // attempt to de-serialize object
794
+ if (parser.createAdditions) {
795
+ IRubyObject vKlassName;
796
+ if (objectDefault) {
797
+ vKlassName = ((RubyHash)result).op_aref(context, parser.createId);
798
+ } else {
799
+ vKlassName = result.callMethod(context, "[]", parser.createId);
800
+ }
801
+
802
+ if (!vKlassName.isNil()) {
803
+ // might throw ArgumentError, we let it propagate
804
+ IRubyObject klass = parser.info.jsonModule.get().
805
+ callMethod(context, "deep_const_get", vKlassName);
806
+ if (klass.respondsTo("json_creatable?") &&
807
+ klass.callMethod(context, "json_creatable?").isTrue()) {
808
+
809
+ returnedResult = klass.callMethod(context, "json_create", result);
810
+ }
811
+ }
812
+ }
813
+ res.update(returnedResult, p + 1);
814
+ }
815
+
816
+ %%{
817
+ machine JSON;
818
+ include JSON_common;
819
+
820
+ write data;
821
+
822
+ action parse_value {
823
+ parseValue(res, fpc, pe);
824
+ if (res.result == null) {
825
+ fhold;
826
+ fbreak;
827
+ } else {
828
+ result = res.result;
829
+ fexec res.p;
830
+ }
831
+ }
832
+
833
+ main := ignore*
834
+ ( begin_value >parse_value)
835
+ ignore*;
836
+ }%%
837
+
838
+ public IRubyObject parseImplemetation() {
839
+ int cs = EVIL;
840
+ int p, pe;
841
+ IRubyObject result = null;
842
+ ParserResult res = new ParserResult();
843
+
844
+ %% write init;
845
+ p = byteList.begin();
846
+ pe = p + byteList.length();
847
+ %% write exec;
848
+
849
+ if (cs >= JSON_first_final && p == pe) {
850
+ return result;
851
+ } else {
852
+ throw unexpectedToken(p, pe);
853
+ }
854
+ }
855
+
856
+ public IRubyObject parse() {
857
+ return parseImplemetation();
858
+ }
859
+
860
+ /**
861
+ * Updates the "view" bytelist with the new offsets and returns it.
862
+ * @param start
863
+ * @param end
864
+ */
865
+ private ByteList absSubSequence(int absStart, int absEnd) {
866
+ view.setBegin(absStart);
867
+ view.setRealSize(absEnd - absStart);
868
+ return view;
869
+ }
870
+
871
+ /**
872
+ * Retrieves a constant directly descended from the <code>JSON</code> module.
873
+ * @param name The constant name
874
+ */
875
+ private IRubyObject getConstant(String name) {
876
+ return parser.info.jsonModule.get().getConstant(name);
877
+ }
878
+
879
+ private RaiseException newException(String className, String message) {
880
+ return Utils.newException(context, className, message);
881
+ }
882
+
883
+ private RaiseException newException(String className, RubyString message) {
884
+ return Utils.newException(context, className, message);
885
+ }
886
+
887
+ private RaiseException newException(String className,
888
+ String messageBegin, ByteList messageEnd) {
889
+ return newException(className,
890
+ getRuntime().newString(messageBegin).cat(messageEnd));
891
+ }
892
+ }
893
+ }