json 2.2.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +51 -0
  3. data/LICENSE +56 -0
  4. data/README.md +17 -1
  5. data/VERSION +1 -1
  6. data/ext/json/ext/generator/generator.c +222 -48
  7. data/ext/json/ext/generator/generator.h +5 -2
  8. data/ext/json/ext/parser/extconf.rb +25 -0
  9. data/ext/json/ext/parser/parser.c +150 -102
  10. data/ext/json/ext/parser/parser.h +1 -0
  11. data/ext/json/ext/parser/parser.rl +80 -32
  12. data/ext/json/extconf.rb +1 -0
  13. data/json.gemspec +0 -0
  14. data/lib/json.rb +549 -29
  15. data/lib/json/add/bigdecimal.rb +2 -2
  16. data/lib/json/add/complex.rb +2 -3
  17. data/lib/json/add/rational.rb +2 -3
  18. data/lib/json/add/regexp.rb +2 -2
  19. data/lib/json/common.rb +370 -125
  20. data/lib/json/pure/generator.rb +31 -10
  21. data/lib/json/pure/parser.rb +31 -5
  22. data/lib/json/version.rb +1 -1
  23. data/tests/fixtures/fail29.json +1 -0
  24. data/tests/fixtures/fail30.json +1 -0
  25. data/tests/fixtures/fail31.json +1 -0
  26. data/tests/fixtures/fail32.json +1 -0
  27. data/tests/json_addition_test.rb +0 -4
  28. data/tests/json_common_interface_test.rb +47 -4
  29. data/tests/json_fixtures_test.rb +9 -1
  30. data/tests/json_generator_test.rb +30 -8
  31. data/tests/json_parser_test.rb +39 -14
  32. data/tests/lib/core_assertions.rb +763 -0
  33. data/tests/lib/envutil.rb +365 -0
  34. data/tests/lib/find_executable.rb +22 -0
  35. data/tests/lib/helper.rb +4 -0
  36. data/tests/ractor_test.rb +30 -0
  37. data/tests/test_helper.rb +3 -3
  38. metadata +30 -40
  39. data/.gitignore +0 -17
  40. data/.travis.yml +0 -23
  41. data/README-json-jruby.md +0 -33
  42. data/Rakefile +0 -408
  43. data/diagrams/.keep +0 -0
  44. data/install.rb +0 -23
  45. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  46. data/java/src/json/ext/Generator.java +0 -443
  47. data/java/src/json/ext/GeneratorMethods.java +0 -231
  48. data/java/src/json/ext/GeneratorService.java +0 -42
  49. data/java/src/json/ext/GeneratorState.java +0 -490
  50. data/java/src/json/ext/OptionsReader.java +0 -113
  51. data/java/src/json/ext/Parser.java +0 -2362
  52. data/java/src/json/ext/Parser.rl +0 -893
  53. data/java/src/json/ext/ParserService.java +0 -34
  54. data/java/src/json/ext/RuntimeInfo.java +0 -116
  55. data/java/src/json/ext/StringDecoder.java +0 -166
  56. data/java/src/json/ext/StringEncoder.java +0 -111
  57. data/java/src/json/ext/Utils.java +0 -88
  58. data/json-java.gemspec +0 -38
  59. data/json_pure.gemspec +0 -38
  60. data/references/rfc7159.txt +0 -899
  61. data/tools/diff.sh +0 -18
  62. data/tools/fuzz.rb +0 -131
  63. data/tools/server.rb +0 -62
@@ -1,893 +0,0 @@
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
- }