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
File without changes
data/install.rb DELETED
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'fileutils'
4
- include FileUtils::Verbose
5
- require 'rbconfig'
6
- include\
7
- begin
8
- RbConfig
9
- rescue NameError
10
- Config
11
- end
12
-
13
- sitelibdir = CONFIG["sitelibdir"]
14
- cd 'lib' do
15
- install('json.rb', sitelibdir)
16
- mkdir_p File.join(sitelibdir, 'json')
17
- for file in Dir['json/**/*}']
18
- d = File.join(sitelibdir, file)
19
- mkdir_p File.dirname(d)
20
- install(file, d)
21
- end
22
- end
23
- warn " *** Installed PURE ruby library."
@@ -1,166 +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.exceptions.RaiseException;
9
- import org.jruby.runtime.ThreadContext;
10
- import org.jruby.util.ByteList;
11
-
12
- /**
13
- * A class specialized in transcoding a certain String format into another,
14
- * using UTF-8 ByteLists as both input and output.
15
- */
16
- abstract class ByteListTranscoder {
17
- protected final ThreadContext context;
18
-
19
- protected ByteList src;
20
- protected int srcEnd;
21
- /** Position where the last read character started */
22
- protected int charStart;
23
- /** Position of the next character to read */
24
- protected int pos;
25
-
26
- private ByteList out;
27
- /**
28
- * When a character that can be copied straight into the output is found,
29
- * its index is stored on this variable, and copying is delayed until
30
- * the sequence of characters that can be copied ends.
31
- *
32
- * <p>The variable stores -1 when not in a plain sequence.
33
- */
34
- private int quoteStart = -1;
35
-
36
- protected ByteListTranscoder(ThreadContext context) {
37
- this.context = context;
38
- }
39
-
40
- protected void init(ByteList src, ByteList out) {
41
- this.init(src, 0, src.length(), out);
42
- }
43
-
44
- protected void init(ByteList src, int start, int end, ByteList out) {
45
- this.src = src;
46
- this.pos = start;
47
- this.charStart = start;
48
- this.srcEnd = end;
49
- this.out = out;
50
- }
51
-
52
- /**
53
- * Returns whether there are any characters left to be read.
54
- */
55
- protected boolean hasNext() {
56
- return pos < srcEnd;
57
- }
58
-
59
- /**
60
- * Returns the next character in the buffer.
61
- */
62
- private char next() {
63
- return src.charAt(pos++);
64
- }
65
-
66
- /**
67
- * Reads an UTF-8 character from the input and returns its code point,
68
- * while advancing the input position.
69
- *
70
- * <p>Raises an {@link #invalidUtf8()} exception if an invalid byte
71
- * is found.
72
- */
73
- protected int readUtf8Char() {
74
- charStart = pos;
75
- char head = next();
76
- if (head <= 0x7f) { // 0b0xxxxxxx (ASCII)
77
- return head;
78
- }
79
- if (head <= 0xbf) { // 0b10xxxxxx
80
- throw invalidUtf8(); // tail byte with no head
81
- }
82
- if (head <= 0xdf) { // 0b110xxxxx
83
- ensureMin(1);
84
- int cp = ((head & 0x1f) << 6)
85
- | nextPart();
86
- if (cp < 0x0080) throw invalidUtf8();
87
- return cp;
88
- }
89
- if (head <= 0xef) { // 0b1110xxxx
90
- ensureMin(2);
91
- int cp = ((head & 0x0f) << 12)
92
- | (nextPart() << 6)
93
- | nextPart();
94
- if (cp < 0x0800) throw invalidUtf8();
95
- return cp;
96
- }
97
- if (head <= 0xf7) { // 0b11110xxx
98
- ensureMin(3);
99
- int cp = ((head & 0x07) << 18)
100
- | (nextPart() << 12)
101
- | (nextPart() << 6)
102
- | nextPart();
103
- if (!Character.isValidCodePoint(cp)) throw invalidUtf8();
104
- return cp;
105
- }
106
- // 0b11111xxx?
107
- throw invalidUtf8();
108
- }
109
-
110
- /**
111
- * Throws a GeneratorError if the input list doesn't have at least this
112
- * many bytes left.
113
- */
114
- protected void ensureMin(int n) {
115
- if (pos + n > srcEnd) throw incompleteUtf8();
116
- }
117
-
118
- /**
119
- * Reads the next byte of a multi-byte UTF-8 character and returns its
120
- * contents (lower 6 bits).
121
- *
122
- * <p>Throws a GeneratorError if the byte is not a valid tail.
123
- */
124
- private int nextPart() {
125
- char c = next();
126
- // tail bytes must be 0b10xxxxxx
127
- if ((c & 0xc0) != 0x80) throw invalidUtf8();
128
- return c & 0x3f;
129
- }
130
-
131
-
132
- protected void quoteStart() {
133
- if (quoteStart == -1) quoteStart = charStart;
134
- }
135
-
136
- /**
137
- * When in a sequence of characters that can be copied directly,
138
- * interrupts the sequence and copies it to the output buffer.
139
- *
140
- * @param endPos The offset until which the direct character quoting should
141
- * occur. You may pass {@link #pos} to quote until the most
142
- * recently read character, or {@link #charStart} to quote
143
- * until the character before it.
144
- */
145
- protected void quoteStop(int endPos) {
146
- if (quoteStart != -1) {
147
- out.append(src, quoteStart, endPos - quoteStart);
148
- quoteStart = -1;
149
- }
150
- }
151
-
152
- protected void append(int b) {
153
- out.append(b);
154
- }
155
-
156
- protected void append(byte[] origin, int start, int length) {
157
- out.append(origin, start, length);
158
- }
159
-
160
-
161
- protected abstract RaiseException invalidUtf8();
162
-
163
- protected RaiseException incompleteUtf8() {
164
- return invalidUtf8();
165
- }
166
- }
@@ -1,443 +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.RubyBignum;
11
- import org.jruby.RubyBoolean;
12
- import org.jruby.RubyClass;
13
- import org.jruby.RubyFixnum;
14
- import org.jruby.RubyFloat;
15
- import org.jruby.RubyHash;
16
- import org.jruby.RubyNumeric;
17
- import org.jruby.RubyString;
18
- import org.jruby.runtime.ThreadContext;
19
- import org.jruby.runtime.builtin.IRubyObject;
20
- import org.jruby.util.ByteList;
21
-
22
- public final class Generator {
23
- private Generator() {
24
- throw new RuntimeException();
25
- }
26
-
27
- /**
28
- * Encodes the given object as a JSON string, using the given handler.
29
- */
30
- static <T extends IRubyObject> RubyString
31
- generateJson(ThreadContext context, T object,
32
- Handler<? super T> handler, IRubyObject[] args) {
33
- Session session = new Session(context, args.length > 0 ? args[0]
34
- : null);
35
- return session.infect(handler.generateNew(session, object));
36
- }
37
-
38
- /**
39
- * Encodes the given object as a JSON string, detecting the appropriate handler
40
- * for the given object.
41
- */
42
- static <T extends IRubyObject> RubyString
43
- generateJson(ThreadContext context, T object, IRubyObject[] args) {
44
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
45
- return generateJson(context, object, handler, args);
46
- }
47
-
48
- /**
49
- * Encodes the given object as a JSON string, using the appropriate
50
- * handler if one is found or calling #to_json if not.
51
- */
52
- public static <T extends IRubyObject> RubyString
53
- generateJson(ThreadContext context, T object,
54
- GeneratorState config) {
55
- Session session = new Session(context, config);
56
- Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
57
- return handler.generateNew(session, object);
58
- }
59
-
60
- /**
61
- * Returns the best serialization handler for the given object.
62
- */
63
- // Java's generics can't handle this satisfactorily, so I'll just leave
64
- // the best I could get and ignore the warnings
65
- @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;
78
- return GENERIC_HANDLER;
79
- }
80
-
81
-
82
- /* Generator context */
83
-
84
- /**
85
- * A class that concentrates all the information that is shared by
86
- * generators working on a single session.
87
- *
88
- * <p>A session is defined as the process of serializing a single root
89
- * object; any handler directly called by container handlers (arrays and
90
- * hashes/objects) shares this object with its caller.
91
- *
92
- * <p>Note that anything called indirectly (via {@link GENERIC_HANDLER})
93
- * won't be part of the session.
94
- */
95
- static class Session {
96
- private final ThreadContext context;
97
- private GeneratorState state;
98
- private IRubyObject possibleState;
99
- private RuntimeInfo info;
100
- private StringEncoder stringEncoder;
101
-
102
- private boolean tainted = false;
103
- private boolean untrusted = false;
104
-
105
- Session(ThreadContext context, GeneratorState state) {
106
- this.context = context;
107
- this.state = state;
108
- }
109
-
110
- Session(ThreadContext context, IRubyObject possibleState) {
111
- this.context = context;
112
- this.possibleState = possibleState == null || possibleState.isNil()
113
- ? null : possibleState;
114
- }
115
-
116
- public ThreadContext getContext() {
117
- return context;
118
- }
119
-
120
- public Ruby getRuntime() {
121
- return context.getRuntime();
122
- }
123
-
124
- public GeneratorState getState() {
125
- if (state == null) {
126
- state = GeneratorState.fromState(context, getInfo(), possibleState);
127
- }
128
- return state;
129
- }
130
-
131
- public RuntimeInfo getInfo() {
132
- if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
133
- return info;
134
- }
135
-
136
- public StringEncoder getStringEncoder() {
137
- if (stringEncoder == null) {
138
- stringEncoder = new StringEncoder(context, getState().asciiOnly());
139
- }
140
- return stringEncoder;
141
- }
142
-
143
- public void infectBy(IRubyObject object) {
144
- if (object.isTaint()) tainted = true;
145
- if (object.isUntrusted()) untrusted = true;
146
- }
147
-
148
- public <T extends IRubyObject> T infect(T object) {
149
- if (tainted) object.setTaint(true);
150
- if (untrusted) object.setUntrusted(true);
151
- return object;
152
- }
153
- }
154
-
155
-
156
- /* Handler base classes */
157
-
158
- private static abstract class Handler<T extends IRubyObject> {
159
- /**
160
- * Returns an estimative of how much space the serialization of the
161
- * given object will take. Used for allocating enough buffer space
162
- * before invoking other methods.
163
- */
164
- int guessSize(Session session, T object) {
165
- return 4;
166
- }
167
-
168
- RubyString generateNew(Session session, T object) {
169
- RubyString result;
170
- ByteList buffer = new ByteList(guessSize(session, object));
171
- generate(session, object, buffer);
172
- result = RubyString.newString(session.getRuntime(), buffer);
173
- ThreadContext context = session.getContext();
174
- RuntimeInfo info = session.getInfo();
175
- result.force_encoding(context, info.utf8.get());
176
- return result;
177
- }
178
-
179
- abstract void generate(Session session, T object, ByteList buffer);
180
- }
181
-
182
- /**
183
- * A handler that returns a fixed keyword regardless of the passed object.
184
- */
185
- private static class KeywordHandler<T extends IRubyObject>
186
- extends Handler<T> {
187
- private final ByteList keyword;
188
-
189
- private KeywordHandler(String keyword) {
190
- this.keyword = new ByteList(ByteList.plain(keyword), false);
191
- }
192
-
193
- @Override
194
- int guessSize(Session session, T object) {
195
- return keyword.length();
196
- }
197
-
198
- @Override
199
- RubyString generateNew(Session session, T object) {
200
- return RubyString.newStringShared(session.getRuntime(), keyword);
201
- }
202
-
203
- @Override
204
- void generate(Session session, T object, ByteList buffer) {
205
- buffer.append(keyword);
206
- }
207
- }
208
-
209
-
210
- /* Handlers */
211
-
212
- static final Handler<RubyBignum> BIGNUM_HANDLER =
213
- new Handler<RubyBignum>() {
214
- @Override
215
- void generate(Session session, RubyBignum object, ByteList buffer) {
216
- // JRUBY-4751: RubyBignum.to_s() returns generic object
217
- // representation (fixed in 1.5, but we maintain backwards
218
- // compatibility; call to_s(IRubyObject[]) then
219
- buffer.append(((RubyString)object.to_s(IRubyObject.NULL_ARRAY)).getByteList());
220
- }
221
- };
222
-
223
- static final Handler<RubyFixnum> FIXNUM_HANDLER =
224
- new Handler<RubyFixnum>() {
225
- @Override
226
- void generate(Session session, RubyFixnum object, ByteList buffer) {
227
- buffer.append(object.to_s().getByteList());
228
- }
229
- };
230
-
231
- static final Handler<RubyFloat> FLOAT_HANDLER =
232
- new Handler<RubyFloat>() {
233
- @Override
234
- void generate(Session session, RubyFloat object, ByteList buffer) {
235
- double value = RubyFloat.num2dbl(object);
236
-
237
- if (Double.isInfinite(value) || Double.isNaN(value)) {
238
- if (!session.getState().allowNaN()) {
239
- throw Utils.newException(session.getContext(),
240
- Utils.M_GENERATOR_ERROR,
241
- object + " not allowed in JSON");
242
- }
243
- }
244
- buffer.append(((RubyString)object.to_s()).getByteList());
245
- }
246
- };
247
-
248
- static final Handler<RubyArray> ARRAY_HANDLER =
249
- new Handler<RubyArray>() {
250
- @Override
251
- int guessSize(Session session, RubyArray object) {
252
- GeneratorState state = session.getState();
253
- int depth = state.getDepth();
254
- int perItem =
255
- 4 // prealloc
256
- + (depth + 1) * state.getIndent().length() // indent
257
- + 1 + state.getArrayNl().length(); // ',' arrayNl
258
- return 2 + object.size() * perItem;
259
- }
260
-
261
- @Override
262
- void generate(Session session, RubyArray object, ByteList buffer) {
263
- ThreadContext context = session.getContext();
264
- Ruby runtime = context.getRuntime();
265
- GeneratorState state = session.getState();
266
- int depth = state.increaseDepth();
267
-
268
- ByteList indentUnit = state.getIndent();
269
- byte[] shift = Utils.repeat(indentUnit, depth);
270
-
271
- ByteList arrayNl = state.getArrayNl();
272
- byte[] delim = new byte[1 + arrayNl.length()];
273
- delim[0] = ',';
274
- System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1,
275
- arrayNl.length());
276
-
277
- session.infectBy(object);
278
-
279
- buffer.append((byte)'[');
280
- buffer.append(arrayNl);
281
- boolean firstItem = true;
282
- for (int i = 0, t = object.getLength(); i < t; i++) {
283
- IRubyObject element = object.eltInternal(i);
284
- session.infectBy(element);
285
- if (firstItem) {
286
- firstItem = false;
287
- } else {
288
- buffer.append(delim);
289
- }
290
- buffer.append(shift);
291
- Handler<IRubyObject> handler = getHandlerFor(runtime, element);
292
- handler.generate(session, element, buffer);
293
- }
294
-
295
- state.decreaseDepth();
296
- if (arrayNl.length() != 0) {
297
- buffer.append(arrayNl);
298
- buffer.append(shift, 0, state.getDepth() * indentUnit.length());
299
- }
300
-
301
- buffer.append((byte)']');
302
- }
303
- };
304
-
305
- static final Handler<RubyHash> HASH_HANDLER =
306
- new Handler<RubyHash>() {
307
- @Override
308
- int guessSize(Session session, RubyHash object) {
309
- GeneratorState state = session.getState();
310
- int perItem =
311
- 12 // key, colon, comma
312
- + (state.getDepth() + 1) * state.getIndent().length()
313
- + state.getSpaceBefore().length()
314
- + state.getSpace().length();
315
- return 2 + object.size() * perItem;
316
- }
317
-
318
- @Override
319
- void generate(final Session session, RubyHash object,
320
- final ByteList buffer) {
321
- ThreadContext context = session.getContext();
322
- final Ruby runtime = context.getRuntime();
323
- final GeneratorState state = session.getState();
324
- final int depth = state.increaseDepth();
325
-
326
- final ByteList objectNl = state.getObjectNl();
327
- final byte[] indent = Utils.repeat(state.getIndent(), depth);
328
- final ByteList spaceBefore = state.getSpaceBefore();
329
- final ByteList space = state.getSpace();
330
-
331
- buffer.append((byte)'{');
332
- buffer.append(objectNl);
333
- object.visitAll(new RubyHash.Visitor() {
334
- private boolean firstPair = true;
335
-
336
- @Override
337
- public void visit(IRubyObject key, IRubyObject value) {
338
- if (firstPair) {
339
- firstPair = false;
340
- } else {
341
- buffer.append((byte)',');
342
- buffer.append(objectNl);
343
- }
344
- if (objectNl.length() != 0) buffer.append(indent);
345
-
346
- STRING_HANDLER.generate(session, key.asString(), buffer);
347
- session.infectBy(key);
348
-
349
- buffer.append(spaceBefore);
350
- buffer.append((byte)':');
351
- buffer.append(space);
352
-
353
- Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value);
354
- valueHandler.generate(session, value, buffer);
355
- session.infectBy(value);
356
- }
357
- });
358
- state.decreaseDepth();
359
- if (objectNl.length() != 0) {
360
- buffer.append(objectNl);
361
- buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
362
- }
363
- buffer.append((byte)'}');
364
- }
365
- };
366
-
367
- static final Handler<RubyString> STRING_HANDLER =
368
- new Handler<RubyString>() {
369
- @Override
370
- int guessSize(Session session, RubyString object) {
371
- // for most applications, most strings will be just a set of
372
- // printable ASCII characters without any escaping, so let's
373
- // just allocate enough space for that + the quotes
374
- return 2 + object.getByteList().length();
375
- }
376
-
377
- @Override
378
- void generate(Session session, RubyString object, ByteList buffer) {
379
- RuntimeInfo info = session.getInfo();
380
- RubyString src;
381
-
382
- if (object.encoding(session.getContext()) != info.utf8.get()) {
383
- src = (RubyString)object.encode(session.getContext(),
384
- info.utf8.get());
385
- } else {
386
- src = object;
387
- }
388
-
389
- session.getStringEncoder().encode(src.getByteList(), buffer);
390
- }
391
- };
392
-
393
- static final Handler<RubyBoolean> TRUE_HANDLER =
394
- new KeywordHandler<RubyBoolean>("true");
395
- static final Handler<RubyBoolean> FALSE_HANDLER =
396
- new KeywordHandler<RubyBoolean>("false");
397
- static final Handler<IRubyObject> NIL_HANDLER =
398
- new KeywordHandler<IRubyObject>("null");
399
-
400
- /**
401
- * The default handler (<code>Object#to_json</code>): coerces the object
402
- * to string using <code>#to_s</code>, and serializes that string.
403
- */
404
- static final Handler<IRubyObject> OBJECT_HANDLER =
405
- new Handler<IRubyObject>() {
406
- @Override
407
- RubyString generateNew(Session session, IRubyObject object) {
408
- RubyString str = object.asString();
409
- return STRING_HANDLER.generateNew(session, str);
410
- }
411
-
412
- @Override
413
- void generate(Session session, IRubyObject object, ByteList buffer) {
414
- RubyString str = object.asString();
415
- STRING_HANDLER.generate(session, str, buffer);
416
- }
417
- };
418
-
419
- /**
420
- * A handler that simply calls <code>#to_json(state)</code> on the
421
- * given object.
422
- */
423
- static final Handler<IRubyObject> GENERIC_HANDLER =
424
- new Handler<IRubyObject>() {
425
- @Override
426
- RubyString generateNew(Session session, IRubyObject object) {
427
- if (object.respondsTo("to_json")) {
428
- IRubyObject result = object.callMethod(session.getContext(), "to_json",
429
- new IRubyObject[] {session.getState()});
430
- if (result instanceof RubyString) return (RubyString)result;
431
- throw session.getRuntime().newTypeError("to_json must return a String");
432
- } else {
433
- return OBJECT_HANDLER.generateNew(session, object);
434
- }
435
- }
436
-
437
- @Override
438
- void generate(Session session, IRubyObject object, ByteList buffer) {
439
- RubyString result = generateNew(session, object);
440
- buffer.append(result.getByteList());
441
- }
442
- };
443
- }