json 2.3.1 → 2.6.2
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.
- checksums.yaml +4 -4
- data/CHANGES.md +38 -5
- data/LICENSE +56 -0
- data/README.md +3 -3
- data/VERSION +1 -1
- data/ext/json/ext/generator/generator.c +61 -22
- data/ext/json/ext/generator/generator.h +5 -2
- data/ext/json/ext/parser/extconf.rb +26 -0
- data/ext/json/ext/parser/parser.c +2980 -1770
- data/ext/json/ext/parser/parser.h +6 -1
- data/ext/json/ext/parser/parser.rl +127 -38
- data/ext/json/extconf.rb +1 -0
- data/json.gemspec +8 -80
- data/lib/json/add/complex.rb +0 -1
- data/lib/json/add/rational.rb +0 -1
- data/lib/json/common.rb +240 -228
- data/lib/json/pure/generator.rb +28 -8
- data/lib/json/pure/parser.rb +21 -3
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +172 -1
- metadata +8 -110
- data/.gitignore +0 -18
- data/.travis.yml +0 -26
- data/Gemfile +0 -14
- data/README-json-jruby.md +0 -33
- data/Rakefile +0 -334
- data/diagrams/.keep +0 -0
- data/install.rb +0 -23
- data/java/src/json/ext/ByteListTranscoder.java +0 -166
- data/java/src/json/ext/Generator.java +0 -466
- data/java/src/json/ext/GeneratorMethods.java +0 -231
- data/java/src/json/ext/GeneratorService.java +0 -42
- data/java/src/json/ext/GeneratorState.java +0 -490
- data/java/src/json/ext/OptionsReader.java +0 -113
- data/java/src/json/ext/Parser.java +0 -2362
- data/java/src/json/ext/Parser.rl +0 -893
- data/java/src/json/ext/ParserService.java +0 -34
- data/java/src/json/ext/RuntimeInfo.java +0 -116
- data/java/src/json/ext/StringDecoder.java +0 -166
- data/java/src/json/ext/StringEncoder.java +0 -111
- data/java/src/json/ext/Utils.java +0 -88
- data/json-java.gemspec +0 -37
- data/json_pure.gemspec +0 -33
- data/lib/json/ext/.keep +0 -0
- data/references/rfc7159.txt +0 -899
- data/tests/fixtures/fail10.json +0 -1
- data/tests/fixtures/fail11.json +0 -1
- data/tests/fixtures/fail12.json +0 -1
- data/tests/fixtures/fail13.json +0 -1
- data/tests/fixtures/fail14.json +0 -1
- data/tests/fixtures/fail18.json +0 -1
- data/tests/fixtures/fail19.json +0 -1
- data/tests/fixtures/fail2.json +0 -1
- data/tests/fixtures/fail20.json +0 -1
- data/tests/fixtures/fail21.json +0 -1
- data/tests/fixtures/fail22.json +0 -1
- data/tests/fixtures/fail23.json +0 -1
- data/tests/fixtures/fail24.json +0 -1
- data/tests/fixtures/fail25.json +0 -1
- data/tests/fixtures/fail27.json +0 -2
- data/tests/fixtures/fail28.json +0 -2
- data/tests/fixtures/fail3.json +0 -1
- data/tests/fixtures/fail4.json +0 -1
- data/tests/fixtures/fail5.json +0 -1
- data/tests/fixtures/fail6.json +0 -1
- data/tests/fixtures/fail7.json +0 -1
- data/tests/fixtures/fail8.json +0 -1
- data/tests/fixtures/fail9.json +0 -1
- data/tests/fixtures/obsolete_fail1.json +0 -1
- data/tests/fixtures/pass1.json +0 -56
- data/tests/fixtures/pass15.json +0 -1
- data/tests/fixtures/pass16.json +0 -1
- data/tests/fixtures/pass17.json +0 -1
- data/tests/fixtures/pass2.json +0 -1
- data/tests/fixtures/pass26.json +0 -1
- data/tests/fixtures/pass3.json +0 -6
- data/tests/json_addition_test.rb +0 -203
- data/tests/json_common_interface_test.rb +0 -126
- data/tests/json_encoding_test.rb +0 -107
- data/tests/json_ext_parser_test.rb +0 -15
- data/tests/json_fixtures_test.rb +0 -37
- data/tests/json_generator_test.rb +0 -421
- data/tests/json_generic_object_test.rb +0 -82
- data/tests/json_parser_test.rb +0 -472
- data/tests/json_string_matching_test.rb +0 -38
- data/tests/test_helper.rb +0 -17
- data/tools/diff.sh +0 -18
- data/tools/fuzz.rb +0 -131
- data/tools/server.rb +0 -62
|
@@ -1,466 +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.RubyBasicObject;
|
|
11
|
-
import org.jruby.RubyBignum;
|
|
12
|
-
import org.jruby.RubyBoolean;
|
|
13
|
-
import org.jruby.RubyClass;
|
|
14
|
-
import org.jruby.RubyFixnum;
|
|
15
|
-
import org.jruby.RubyFloat;
|
|
16
|
-
import org.jruby.RubyHash;
|
|
17
|
-
import org.jruby.RubyNumeric;
|
|
18
|
-
import org.jruby.RubyString;
|
|
19
|
-
import org.jruby.runtime.ClassIndex;
|
|
20
|
-
import org.jruby.runtime.ThreadContext;
|
|
21
|
-
import org.jruby.runtime.builtin.IRubyObject;
|
|
22
|
-
import org.jruby.util.ByteList;
|
|
23
|
-
|
|
24
|
-
public final class Generator {
|
|
25
|
-
private Generator() {
|
|
26
|
-
throw new RuntimeException();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Encodes the given object as a JSON string, using the given handler.
|
|
31
|
-
*/
|
|
32
|
-
static <T extends IRubyObject> RubyString
|
|
33
|
-
generateJson(ThreadContext context, T object,
|
|
34
|
-
Handler<? super T> handler, IRubyObject[] args) {
|
|
35
|
-
Session session = new Session(context, args.length > 0 ? args[0]
|
|
36
|
-
: null);
|
|
37
|
-
return session.infect(handler.generateNew(session, object));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Encodes the given object as a JSON string, detecting the appropriate handler
|
|
42
|
-
* for the given object.
|
|
43
|
-
*/
|
|
44
|
-
static <T extends IRubyObject> RubyString
|
|
45
|
-
generateJson(ThreadContext context, T object, IRubyObject[] args) {
|
|
46
|
-
Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
|
|
47
|
-
return generateJson(context, object, handler, args);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Encodes the given object as a JSON string, using the appropriate
|
|
52
|
-
* handler if one is found or calling #to_json if not.
|
|
53
|
-
*/
|
|
54
|
-
public static <T extends IRubyObject> RubyString
|
|
55
|
-
generateJson(ThreadContext context, T object,
|
|
56
|
-
GeneratorState config) {
|
|
57
|
-
Session session = new Session(context, config);
|
|
58
|
-
Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
|
|
59
|
-
return handler.generateNew(session, object);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// NOTE: drop this once Ruby 1.9.3 support is gone!
|
|
63
|
-
private static final int FIXNUM = 1;
|
|
64
|
-
private static final int BIGNUM = 2;
|
|
65
|
-
private static final int ARRAY = 3;
|
|
66
|
-
private static final int STRING = 4;
|
|
67
|
-
private static final int NIL = 5;
|
|
68
|
-
private static final int TRUE = 6;
|
|
69
|
-
private static final int FALSE = 7;
|
|
70
|
-
private static final int HASH = 10;
|
|
71
|
-
private static final int FLOAT = 11;
|
|
72
|
-
// hard-coded due JRuby 1.7 compatibility
|
|
73
|
-
// https://github.com/jruby/jruby/blob/1.7.27/core/src/main/java/org/jruby/runtime/ClassIndex.java
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Returns the best serialization handler for the given object.
|
|
77
|
-
*/
|
|
78
|
-
// Java's generics can't handle this satisfactorily, so I'll just leave
|
|
79
|
-
// the best I could get and ignore the warnings
|
|
80
|
-
@SuppressWarnings("unchecked")
|
|
81
|
-
private static <T extends IRubyObject>
|
|
82
|
-
Handler<? super T> getHandlerFor(Ruby runtime, T object) {
|
|
83
|
-
switch (((RubyBasicObject) object).getNativeTypeIndex()) {
|
|
84
|
-
// can not use getNativeClassIndex due 1.7 compatibility
|
|
85
|
-
case NIL : return (Handler) NIL_HANDLER;
|
|
86
|
-
case TRUE : return (Handler) TRUE_HANDLER;
|
|
87
|
-
case FALSE : return (Handler) FALSE_HANDLER;
|
|
88
|
-
case FLOAT : return (Handler) FLOAT_HANDLER;
|
|
89
|
-
case FIXNUM : return (Handler) FIXNUM_HANDLER;
|
|
90
|
-
case BIGNUM : return (Handler) BIGNUM_HANDLER;
|
|
91
|
-
case STRING :
|
|
92
|
-
if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break;
|
|
93
|
-
return (Handler) STRING_HANDLER;
|
|
94
|
-
case ARRAY :
|
|
95
|
-
if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break;
|
|
96
|
-
return (Handler) ARRAY_HANDLER;
|
|
97
|
-
case HASH :
|
|
98
|
-
if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break;
|
|
99
|
-
return (Handler) HASH_HANDLER;
|
|
100
|
-
}
|
|
101
|
-
return GENERIC_HANDLER;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/* Generator context */
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* A class that concentrates all the information that is shared by
|
|
109
|
-
* generators working on a single session.
|
|
110
|
-
*
|
|
111
|
-
* <p>A session is defined as the process of serializing a single root
|
|
112
|
-
* object; any handler directly called by container handlers (arrays and
|
|
113
|
-
* hashes/objects) shares this object with its caller.
|
|
114
|
-
*
|
|
115
|
-
* <p>Note that anything called indirectly (via {@link GENERIC_HANDLER})
|
|
116
|
-
* won't be part of the session.
|
|
117
|
-
*/
|
|
118
|
-
static class Session {
|
|
119
|
-
private final ThreadContext context;
|
|
120
|
-
private GeneratorState state;
|
|
121
|
-
private IRubyObject possibleState;
|
|
122
|
-
private RuntimeInfo info;
|
|
123
|
-
private StringEncoder stringEncoder;
|
|
124
|
-
|
|
125
|
-
private boolean tainted = false;
|
|
126
|
-
private boolean untrusted = false;
|
|
127
|
-
|
|
128
|
-
Session(ThreadContext context, GeneratorState state) {
|
|
129
|
-
this.context = context;
|
|
130
|
-
this.state = state;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
Session(ThreadContext context, IRubyObject possibleState) {
|
|
134
|
-
this.context = context;
|
|
135
|
-
this.possibleState = possibleState == null || possibleState.isNil()
|
|
136
|
-
? null : possibleState;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public ThreadContext getContext() {
|
|
140
|
-
return context;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
public Ruby getRuntime() {
|
|
144
|
-
return context.getRuntime();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
public GeneratorState getState() {
|
|
148
|
-
if (state == null) {
|
|
149
|
-
state = GeneratorState.fromState(context, getInfo(), possibleState);
|
|
150
|
-
}
|
|
151
|
-
return state;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
public RuntimeInfo getInfo() {
|
|
155
|
-
if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
|
|
156
|
-
return info;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public StringEncoder getStringEncoder() {
|
|
160
|
-
if (stringEncoder == null) {
|
|
161
|
-
stringEncoder = new StringEncoder(context, getState().asciiOnly());
|
|
162
|
-
}
|
|
163
|
-
return stringEncoder;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public void infectBy(IRubyObject object) {
|
|
167
|
-
if (object.isTaint()) tainted = true;
|
|
168
|
-
if (object.isUntrusted()) untrusted = true;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
public <T extends IRubyObject> T infect(T object) {
|
|
172
|
-
if (tainted) object.setTaint(true);
|
|
173
|
-
if (untrusted) object.setUntrusted(true);
|
|
174
|
-
return object;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
/* Handler base classes */
|
|
180
|
-
|
|
181
|
-
private static abstract class Handler<T extends IRubyObject> {
|
|
182
|
-
/**
|
|
183
|
-
* Returns an estimative of how much space the serialization of the
|
|
184
|
-
* given object will take. Used for allocating enough buffer space
|
|
185
|
-
* before invoking other methods.
|
|
186
|
-
*/
|
|
187
|
-
int guessSize(Session session, T object) {
|
|
188
|
-
return 4;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
RubyString generateNew(Session session, T object) {
|
|
192
|
-
RubyString result;
|
|
193
|
-
ByteList buffer = new ByteList(guessSize(session, object));
|
|
194
|
-
generate(session, object, buffer);
|
|
195
|
-
result = RubyString.newString(session.getRuntime(), buffer);
|
|
196
|
-
ThreadContext context = session.getContext();
|
|
197
|
-
RuntimeInfo info = session.getInfo();
|
|
198
|
-
result.force_encoding(context, info.utf8.get());
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
abstract void generate(Session session, T object, ByteList buffer);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* A handler that returns a fixed keyword regardless of the passed object.
|
|
207
|
-
*/
|
|
208
|
-
private static class KeywordHandler<T extends IRubyObject>
|
|
209
|
-
extends Handler<T> {
|
|
210
|
-
private final ByteList keyword;
|
|
211
|
-
|
|
212
|
-
private KeywordHandler(String keyword) {
|
|
213
|
-
this.keyword = new ByteList(ByteList.plain(keyword), false);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
@Override
|
|
217
|
-
int guessSize(Session session, T object) {
|
|
218
|
-
return keyword.length();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
@Override
|
|
222
|
-
RubyString generateNew(Session session, T object) {
|
|
223
|
-
return RubyString.newStringShared(session.getRuntime(), keyword);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
@Override
|
|
227
|
-
void generate(Session session, T object, ByteList buffer) {
|
|
228
|
-
buffer.append(keyword);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
/* Handlers */
|
|
234
|
-
|
|
235
|
-
static final Handler<RubyBignum> BIGNUM_HANDLER =
|
|
236
|
-
new Handler<RubyBignum>() {
|
|
237
|
-
@Override
|
|
238
|
-
void generate(Session session, RubyBignum object, ByteList buffer) {
|
|
239
|
-
// JRUBY-4751: RubyBignum.to_s() returns generic object
|
|
240
|
-
// representation (fixed in 1.5, but we maintain backwards
|
|
241
|
-
// compatibility; call to_s(IRubyObject[]) then
|
|
242
|
-
buffer.append(((RubyString)object.to_s(IRubyObject.NULL_ARRAY)).getByteList());
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
static final Handler<RubyFixnum> FIXNUM_HANDLER =
|
|
247
|
-
new Handler<RubyFixnum>() {
|
|
248
|
-
@Override
|
|
249
|
-
void generate(Session session, RubyFixnum object, ByteList buffer) {
|
|
250
|
-
buffer.append(object.to_s().getByteList());
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
static final Handler<RubyFloat> FLOAT_HANDLER =
|
|
255
|
-
new Handler<RubyFloat>() {
|
|
256
|
-
@Override
|
|
257
|
-
void generate(Session session, RubyFloat object, ByteList buffer) {
|
|
258
|
-
double value = RubyFloat.num2dbl(object);
|
|
259
|
-
|
|
260
|
-
if (Double.isInfinite(value) || Double.isNaN(value)) {
|
|
261
|
-
if (!session.getState().allowNaN()) {
|
|
262
|
-
throw Utils.newException(session.getContext(),
|
|
263
|
-
Utils.M_GENERATOR_ERROR,
|
|
264
|
-
object + " not allowed in JSON");
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
buffer.append(((RubyString)object.to_s()).getByteList());
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
static final Handler<RubyArray> ARRAY_HANDLER =
|
|
272
|
-
new Handler<RubyArray>() {
|
|
273
|
-
@Override
|
|
274
|
-
int guessSize(Session session, RubyArray object) {
|
|
275
|
-
GeneratorState state = session.getState();
|
|
276
|
-
int depth = state.getDepth();
|
|
277
|
-
int perItem =
|
|
278
|
-
4 // prealloc
|
|
279
|
-
+ (depth + 1) * state.getIndent().length() // indent
|
|
280
|
-
+ 1 + state.getArrayNl().length(); // ',' arrayNl
|
|
281
|
-
return 2 + object.size() * perItem;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
@Override
|
|
285
|
-
void generate(Session session, RubyArray object, ByteList buffer) {
|
|
286
|
-
ThreadContext context = session.getContext();
|
|
287
|
-
Ruby runtime = context.getRuntime();
|
|
288
|
-
GeneratorState state = session.getState();
|
|
289
|
-
int depth = state.increaseDepth();
|
|
290
|
-
|
|
291
|
-
ByteList indentUnit = state.getIndent();
|
|
292
|
-
byte[] shift = Utils.repeat(indentUnit, depth);
|
|
293
|
-
|
|
294
|
-
ByteList arrayNl = state.getArrayNl();
|
|
295
|
-
byte[] delim = new byte[1 + arrayNl.length()];
|
|
296
|
-
delim[0] = ',';
|
|
297
|
-
System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1,
|
|
298
|
-
arrayNl.length());
|
|
299
|
-
|
|
300
|
-
session.infectBy(object);
|
|
301
|
-
|
|
302
|
-
buffer.append((byte)'[');
|
|
303
|
-
buffer.append(arrayNl);
|
|
304
|
-
boolean firstItem = true;
|
|
305
|
-
for (int i = 0, t = object.getLength(); i < t; i++) {
|
|
306
|
-
IRubyObject element = object.eltInternal(i);
|
|
307
|
-
session.infectBy(element);
|
|
308
|
-
if (firstItem) {
|
|
309
|
-
firstItem = false;
|
|
310
|
-
} else {
|
|
311
|
-
buffer.append(delim);
|
|
312
|
-
}
|
|
313
|
-
buffer.append(shift);
|
|
314
|
-
Handler<IRubyObject> handler = getHandlerFor(runtime, element);
|
|
315
|
-
handler.generate(session, element, buffer);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
state.decreaseDepth();
|
|
319
|
-
if (arrayNl.length() != 0) {
|
|
320
|
-
buffer.append(arrayNl);
|
|
321
|
-
buffer.append(shift, 0, state.getDepth() * indentUnit.length());
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
buffer.append((byte)']');
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
static final Handler<RubyHash> HASH_HANDLER =
|
|
329
|
-
new Handler<RubyHash>() {
|
|
330
|
-
@Override
|
|
331
|
-
int guessSize(Session session, RubyHash object) {
|
|
332
|
-
GeneratorState state = session.getState();
|
|
333
|
-
int perItem =
|
|
334
|
-
12 // key, colon, comma
|
|
335
|
-
+ (state.getDepth() + 1) * state.getIndent().length()
|
|
336
|
-
+ state.getSpaceBefore().length()
|
|
337
|
-
+ state.getSpace().length();
|
|
338
|
-
return 2 + object.size() * perItem;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
@Override
|
|
342
|
-
void generate(final Session session, RubyHash object,
|
|
343
|
-
final ByteList buffer) {
|
|
344
|
-
ThreadContext context = session.getContext();
|
|
345
|
-
final Ruby runtime = context.getRuntime();
|
|
346
|
-
final GeneratorState state = session.getState();
|
|
347
|
-
final int depth = state.increaseDepth();
|
|
348
|
-
|
|
349
|
-
final ByteList objectNl = state.getObjectNl();
|
|
350
|
-
final byte[] indent = Utils.repeat(state.getIndent(), depth);
|
|
351
|
-
final ByteList spaceBefore = state.getSpaceBefore();
|
|
352
|
-
final ByteList space = state.getSpace();
|
|
353
|
-
|
|
354
|
-
buffer.append((byte)'{');
|
|
355
|
-
buffer.append(objectNl);
|
|
356
|
-
object.visitAll(new RubyHash.Visitor() {
|
|
357
|
-
private boolean firstPair = true;
|
|
358
|
-
|
|
359
|
-
@Override
|
|
360
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
|
361
|
-
if (firstPair) {
|
|
362
|
-
firstPair = false;
|
|
363
|
-
} else {
|
|
364
|
-
buffer.append((byte)',');
|
|
365
|
-
buffer.append(objectNl);
|
|
366
|
-
}
|
|
367
|
-
if (objectNl.length() != 0) buffer.append(indent);
|
|
368
|
-
|
|
369
|
-
STRING_HANDLER.generate(session, key.asString(), buffer);
|
|
370
|
-
session.infectBy(key);
|
|
371
|
-
|
|
372
|
-
buffer.append(spaceBefore);
|
|
373
|
-
buffer.append((byte)':');
|
|
374
|
-
buffer.append(space);
|
|
375
|
-
|
|
376
|
-
Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value);
|
|
377
|
-
valueHandler.generate(session, value, buffer);
|
|
378
|
-
session.infectBy(value);
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
state.decreaseDepth();
|
|
382
|
-
if (objectNl.length() != 0) {
|
|
383
|
-
buffer.append(objectNl);
|
|
384
|
-
buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
|
|
385
|
-
}
|
|
386
|
-
buffer.append((byte)'}');
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
static final Handler<RubyString> STRING_HANDLER =
|
|
391
|
-
new Handler<RubyString>() {
|
|
392
|
-
@Override
|
|
393
|
-
int guessSize(Session session, RubyString object) {
|
|
394
|
-
// for most applications, most strings will be just a set of
|
|
395
|
-
// printable ASCII characters without any escaping, so let's
|
|
396
|
-
// just allocate enough space for that + the quotes
|
|
397
|
-
return 2 + object.getByteList().length();
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
@Override
|
|
401
|
-
void generate(Session session, RubyString object, ByteList buffer) {
|
|
402
|
-
RuntimeInfo info = session.getInfo();
|
|
403
|
-
RubyString src;
|
|
404
|
-
|
|
405
|
-
if (object.encoding(session.getContext()) != info.utf8.get()) {
|
|
406
|
-
src = (RubyString)object.encode(session.getContext(),
|
|
407
|
-
info.utf8.get());
|
|
408
|
-
} else {
|
|
409
|
-
src = object;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
session.getStringEncoder().encode(src.getByteList(), buffer);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
static final Handler<RubyBoolean> TRUE_HANDLER =
|
|
417
|
-
new KeywordHandler<RubyBoolean>("true");
|
|
418
|
-
static final Handler<RubyBoolean> FALSE_HANDLER =
|
|
419
|
-
new KeywordHandler<RubyBoolean>("false");
|
|
420
|
-
static final Handler<IRubyObject> NIL_HANDLER =
|
|
421
|
-
new KeywordHandler<IRubyObject>("null");
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* The default handler (<code>Object#to_json</code>): coerces the object
|
|
425
|
-
* to string using <code>#to_s</code>, and serializes that string.
|
|
426
|
-
*/
|
|
427
|
-
static final Handler<IRubyObject> OBJECT_HANDLER =
|
|
428
|
-
new Handler<IRubyObject>() {
|
|
429
|
-
@Override
|
|
430
|
-
RubyString generateNew(Session session, IRubyObject object) {
|
|
431
|
-
RubyString str = object.asString();
|
|
432
|
-
return STRING_HANDLER.generateNew(session, str);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
@Override
|
|
436
|
-
void generate(Session session, IRubyObject object, ByteList buffer) {
|
|
437
|
-
RubyString str = object.asString();
|
|
438
|
-
STRING_HANDLER.generate(session, str, buffer);
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* A handler that simply calls <code>#to_json(state)</code> on the
|
|
444
|
-
* given object.
|
|
445
|
-
*/
|
|
446
|
-
static final Handler<IRubyObject> GENERIC_HANDLER =
|
|
447
|
-
new Handler<IRubyObject>() {
|
|
448
|
-
@Override
|
|
449
|
-
RubyString generateNew(Session session, IRubyObject object) {
|
|
450
|
-
if (object.respondsTo("to_json")) {
|
|
451
|
-
IRubyObject result = object.callMethod(session.getContext(), "to_json",
|
|
452
|
-
new IRubyObject[] {session.getState()});
|
|
453
|
-
if (result instanceof RubyString) return (RubyString)result;
|
|
454
|
-
throw session.getRuntime().newTypeError("to_json must return a String");
|
|
455
|
-
} else {
|
|
456
|
-
return OBJECT_HANDLER.generateNew(session, object);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
@Override
|
|
461
|
-
void generate(Session session, IRubyObject object, ByteList buffer) {
|
|
462
|
-
RubyString result = generateNew(session, object);
|
|
463
|
-
buffer.append(result.getByteList());
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
}
|
|
@@ -1,231 +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 java.lang.ref.WeakReference;
|
|
9
|
-
import org.jruby.Ruby;
|
|
10
|
-
import org.jruby.RubyArray;
|
|
11
|
-
import org.jruby.RubyBoolean;
|
|
12
|
-
import org.jruby.RubyFixnum;
|
|
13
|
-
import org.jruby.RubyFloat;
|
|
14
|
-
import org.jruby.RubyHash;
|
|
15
|
-
import org.jruby.RubyInteger;
|
|
16
|
-
import org.jruby.RubyModule;
|
|
17
|
-
import org.jruby.RubyNumeric;
|
|
18
|
-
import org.jruby.RubyString;
|
|
19
|
-
import org.jruby.anno.JRubyMethod;
|
|
20
|
-
import org.jruby.runtime.ThreadContext;
|
|
21
|
-
import org.jruby.runtime.builtin.IRubyObject;
|
|
22
|
-
import org.jruby.util.ByteList;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* A class that populates the
|
|
26
|
-
* <code>Json::Ext::Generator::GeneratorMethods</code> module.
|
|
27
|
-
*
|
|
28
|
-
* @author mernen
|
|
29
|
-
*/
|
|
30
|
-
class GeneratorMethods {
|
|
31
|
-
/**
|
|
32
|
-
* Populates the given module with all modules and their methods
|
|
33
|
-
* @param info
|
|
34
|
-
* @param generatorMethodsModule The module to populate
|
|
35
|
-
* (normally <code>JSON::Generator::GeneratorMethods</code>)
|
|
36
|
-
*/
|
|
37
|
-
static void populate(RuntimeInfo info, RubyModule module) {
|
|
38
|
-
defineMethods(module, "Array", RbArray.class);
|
|
39
|
-
defineMethods(module, "FalseClass", RbFalse.class);
|
|
40
|
-
defineMethods(module, "Float", RbFloat.class);
|
|
41
|
-
defineMethods(module, "Hash", RbHash.class);
|
|
42
|
-
defineMethods(module, "Integer", RbInteger.class);
|
|
43
|
-
defineMethods(module, "NilClass", RbNil.class);
|
|
44
|
-
defineMethods(module, "Object", RbObject.class);
|
|
45
|
-
defineMethods(module, "String", RbString.class);
|
|
46
|
-
defineMethods(module, "TrueClass", RbTrue.class);
|
|
47
|
-
|
|
48
|
-
info.stringExtendModule = new WeakReference<RubyModule>(module.defineModuleUnder("String")
|
|
49
|
-
.defineModuleUnder("Extend"));
|
|
50
|
-
info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Convenience method for defining methods on a submodule.
|
|
55
|
-
* @param parentModule
|
|
56
|
-
* @param submoduleName
|
|
57
|
-
* @param klass
|
|
58
|
-
*/
|
|
59
|
-
private static void defineMethods(RubyModule parentModule,
|
|
60
|
-
String submoduleName, Class klass) {
|
|
61
|
-
RubyModule submodule = parentModule.defineModuleUnder(submoduleName);
|
|
62
|
-
submodule.defineAnnotatedMethods(klass);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
public static class RbHash {
|
|
67
|
-
@JRubyMethod(rest=true)
|
|
68
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
69
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
70
|
-
return Generator.generateJson(context, (RubyHash)vSelf,
|
|
71
|
-
Generator.HASH_HANDLER, args);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public static class RbArray {
|
|
76
|
-
@JRubyMethod(rest=true)
|
|
77
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
78
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
79
|
-
return Generator.generateJson(context, (RubyArray)vSelf,
|
|
80
|
-
Generator.ARRAY_HANDLER, args);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public static class RbInteger {
|
|
85
|
-
@JRubyMethod(rest=true)
|
|
86
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
87
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
88
|
-
return Generator.generateJson(context, vSelf, args);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public static class RbFloat {
|
|
93
|
-
@JRubyMethod(rest=true)
|
|
94
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
95
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
96
|
-
return Generator.generateJson(context, (RubyFloat)vSelf,
|
|
97
|
-
Generator.FLOAT_HANDLER, args);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
public static class RbString {
|
|
102
|
-
@JRubyMethod(rest=true)
|
|
103
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
104
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
105
|
-
return Generator.generateJson(context, (RubyString)vSelf,
|
|
106
|
-
Generator.STRING_HANDLER, args);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* <code>{@link RubyString String}#to_json_raw(*)</code>
|
|
111
|
-
*
|
|
112
|
-
* <p>This method creates a JSON text from the result of a call to
|
|
113
|
-
* {@link #to_json_raw_object} of this String.
|
|
114
|
-
*/
|
|
115
|
-
@JRubyMethod(rest=true)
|
|
116
|
-
public static IRubyObject to_json_raw(ThreadContext context,
|
|
117
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
118
|
-
RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf));
|
|
119
|
-
return Generator.generateJson(context, obj,
|
|
120
|
-
Generator.HASH_HANDLER, args);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* <code>{@link RubyString String}#to_json_raw_object(*)</code>
|
|
125
|
-
*
|
|
126
|
-
* <p>This method creates a raw object Hash, that can be nested into
|
|
127
|
-
* other data structures and will be unparsed as a raw string. This
|
|
128
|
-
* method should be used if you want to convert raw strings to JSON
|
|
129
|
-
* instead of UTF-8 strings, e.g. binary data.
|
|
130
|
-
*/
|
|
131
|
-
@JRubyMethod(rest=true)
|
|
132
|
-
public static IRubyObject to_json_raw_object(ThreadContext context,
|
|
133
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
134
|
-
return toJsonRawObject(context, Utils.ensureString(vSelf));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private static RubyHash toJsonRawObject(ThreadContext context,
|
|
138
|
-
RubyString self) {
|
|
139
|
-
Ruby runtime = context.getRuntime();
|
|
140
|
-
RubyHash result = RubyHash.newHash(runtime);
|
|
141
|
-
|
|
142
|
-
IRubyObject createId = RuntimeInfo.forRuntime(runtime)
|
|
143
|
-
.jsonModule.get().callMethod(context, "create_id");
|
|
144
|
-
result.op_aset(context, createId, self.getMetaClass().to_s());
|
|
145
|
-
|
|
146
|
-
ByteList bl = self.getByteList();
|
|
147
|
-
byte[] uBytes = bl.unsafeBytes();
|
|
148
|
-
RubyArray array = runtime.newArray(bl.length());
|
|
149
|
-
for (int i = bl.begin(), t = bl.begin() + bl.length(); i < t; i++) {
|
|
150
|
-
array.store(i, runtime.newFixnum(uBytes[i] & 0xff));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
result.op_aset(context, runtime.newString("raw"), array);
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@JRubyMethod(required=1, module=true)
|
|
158
|
-
public static IRubyObject included(ThreadContext context,
|
|
159
|
-
IRubyObject vSelf, IRubyObject module) {
|
|
160
|
-
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
|
|
161
|
-
return module.callMethod(context, "extend", info.stringExtendModule.get());
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
public static class StringExtend {
|
|
166
|
-
/**
|
|
167
|
-
* <code>{@link RubyString String}#json_create(o)</code>
|
|
168
|
-
*
|
|
169
|
-
* <p>Raw Strings are JSON Objects (the raw bytes are stored in an
|
|
170
|
-
* array for the key "raw"). The Ruby String can be created by this
|
|
171
|
-
* module method.
|
|
172
|
-
*/
|
|
173
|
-
@JRubyMethod(required=1)
|
|
174
|
-
public static IRubyObject json_create(ThreadContext context,
|
|
175
|
-
IRubyObject vSelf, IRubyObject vHash) {
|
|
176
|
-
Ruby runtime = context.getRuntime();
|
|
177
|
-
RubyHash o = vHash.convertToHash();
|
|
178
|
-
IRubyObject rawData = o.fastARef(runtime.newString("raw"));
|
|
179
|
-
if (rawData == null) {
|
|
180
|
-
throw runtime.newArgumentError("\"raw\" value not defined "
|
|
181
|
-
+ "for encoded String");
|
|
182
|
-
}
|
|
183
|
-
RubyArray ary = Utils.ensureArray(rawData);
|
|
184
|
-
byte[] bytes = new byte[ary.getLength()];
|
|
185
|
-
for (int i = 0, t = ary.getLength(); i < t; i++) {
|
|
186
|
-
IRubyObject element = ary.eltInternal(i);
|
|
187
|
-
if (element instanceof RubyFixnum) {
|
|
188
|
-
bytes[i] = (byte)RubyNumeric.fix2long(element);
|
|
189
|
-
} else {
|
|
190
|
-
throw runtime.newTypeError(element, runtime.getFixnum());
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return runtime.newString(new ByteList(bytes, false));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
public static class RbTrue {
|
|
198
|
-
@JRubyMethod(rest=true)
|
|
199
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
200
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
201
|
-
return Generator.generateJson(context, (RubyBoolean)vSelf,
|
|
202
|
-
Generator.TRUE_HANDLER, args);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
public static class RbFalse {
|
|
207
|
-
@JRubyMethod(rest=true)
|
|
208
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
209
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
210
|
-
return Generator.generateJson(context, (RubyBoolean)vSelf,
|
|
211
|
-
Generator.FALSE_HANDLER, args);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
public static class RbNil {
|
|
216
|
-
@JRubyMethod(rest=true)
|
|
217
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
218
|
-
IRubyObject vSelf, IRubyObject[] args) {
|
|
219
|
-
return Generator.generateJson(context, vSelf,
|
|
220
|
-
Generator.NIL_HANDLER, args);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
public static class RbObject {
|
|
225
|
-
@JRubyMethod(rest=true)
|
|
226
|
-
public static IRubyObject to_json(ThreadContext context,
|
|
227
|
-
IRubyObject self, IRubyObject[] args) {
|
|
228
|
-
return RbString.to_json(context, self.asString(), args);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|