json 1.5.3 → 1.5.4

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.

@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
- *
3
+ *
4
4
  * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
5
  * for details.
6
6
  */
@@ -29,16 +29,16 @@ import org.jruby.util.ByteList;
29
29
 
30
30
  /**
31
31
  * The <code>JSON::Ext::Parser</code> class.
32
- *
32
+ *
33
33
  * <p>This is the JSON parser implemented as a Java class. To use it as the
34
34
  * standard parser, set
35
35
  * <pre>JSON.parser = JSON::Ext::Parser</pre>
36
36
  * This is performed for you when you <code>include "json/ext"</code>.
37
- *
37
+ *
38
38
  * <p>This class does not perform the actual parsing, just acts as an interface
39
39
  * to Ruby code. When the {@link #parse()} method is invoked, a
40
40
  * Parser.ParserSession object is instantiated, which handles the process.
41
- *
41
+ *
42
42
  * @author mernen
43
43
  */
44
44
  public class Parser extends RubyObject {
@@ -49,6 +49,7 @@ public class Parser extends RubyObject {
49
49
  private int maxNesting;
50
50
  private boolean allowNaN;
51
51
  private boolean symbolizeNames;
52
+ private boolean quirksMode;
52
53
  private RubyClass objectClass;
53
54
  private RubyClass arrayClass;
54
55
  private RubyHash match_string;
@@ -69,7 +70,7 @@ public class Parser extends RubyObject {
69
70
 
70
71
  /**
71
72
  * Multiple-value return for internal parser methods.
72
- *
73
+ *
73
74
  * <p>All the <code>parse<var>Stuff</var></code> methods return instances of
74
75
  * <code>ParserResult</code> when successful, or <code>null</code> when
75
76
  * there's a problem with the input data.
@@ -98,18 +99,18 @@ public class Parser extends RubyObject {
98
99
 
99
100
  /**
100
101
  * <code>Parser.new(source, opts = {})</code>
101
- *
102
+ *
102
103
  * <p>Creates a new <code>JSON::Ext::Parser</code> instance for the string
103
104
  * <code>source</code>.
104
105
  * It will be configured by the <code>opts</code> Hash.
105
106
  * <code>opts</code> can have the following keys:
106
- *
107
+ *
107
108
  * <dl>
108
109
  * <dt><code>:max_nesting</code>
109
110
  * <dd>The maximum depth of nesting allowed in the parsed data
110
111
  * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>,
111
112
  * it defaults to 19.
112
- *
113
+ *
113
114
  * <dt><code>:allow_nan</code>
114
115
  * <dd>If set to <code>true</code>, allow <code>NaN</code>,
115
116
  * <code>Infinity</code> and <code>-Infinity</code> in defiance of RFC 4627
@@ -118,17 +119,25 @@ public class Parser extends RubyObject {
118
119
  * <dt><code>:symbolize_names</code>
119
120
  * <dd>If set to <code>true</code>, returns symbols for the names (keys) in
120
121
  * a JSON object. Otherwise strings are returned, which is also the default.
122
+ *
123
+ * <dt><code>:quirks_mode?</code>
124
+ * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false
125
+ * otherwise.
121
126
  *
122
127
  * <dt><code>:create_additions</code>
123
128
  * <dd>If set to <code>false</code>, the Parser doesn't create additions
124
129
  * even if a matchin class and <code>create_id</code> was found. This option
125
130
  * defaults to <code>true</code>.
126
- *
131
+ *
127
132
  * <dt><code>:object_class</code>
128
133
  * <dd>Defaults to Hash.
129
- *
134
+ *
130
135
  * <dt><code>:array_class</code>
131
136
  * <dd>Defaults to Array.
137
+ *
138
+ * <dt><code>:quirks_mode</code>
139
+ * <dd>Enables quirks_mode for parser, that is for example parsing single
140
+ * JSON values instead of documents is possible.
132
141
  * </dl>
133
142
  */
134
143
  @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
@@ -142,20 +151,25 @@ public class Parser extends RubyObject {
142
151
 
143
152
  @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
144
153
  public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
145
- Ruby runtime = context.getRuntime();
146
- RubyString source = convertEncoding(context, args[0].convertToString());
154
+ Ruby runtime = context.getRuntime();
155
+ if (this.vSource != null) {
156
+ throw runtime.newTypeError("already initialized instance");
157
+ }
147
158
 
148
159
  OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
149
160
  this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING);
150
161
  this.allowNaN = opts.getBool("allow_nan", false);
151
162
  this.symbolizeNames = opts.getBool("symbolize_names", false);
163
+ this.quirksMode = opts.getBool("quirks_mode", false);
152
164
  this.createId = opts.getString("create_id", getCreateId(context));
153
165
  this.createAdditions = opts.getBool("create_additions", true);
154
166
  this.objectClass = opts.getClass("object_class", runtime.getHash());
155
167
  this.arrayClass = opts.getClass("array_class", runtime.getArray());
156
168
  this.match_string = opts.getHash("match_string");
157
169
 
158
- this.vSource = source;
170
+ this.vSource = args[0].convertToString();
171
+ if (!quirksMode) this.vSource = convertEncoding(context, vSource);
172
+
159
173
  return this;
160
174
  }
161
175
 
@@ -174,8 +188,8 @@ public class Parser extends RubyObject {
174
188
 
175
189
  if (info.encodingsSupported()) {
176
190
  RubyEncoding encoding = (RubyEncoding)source.encoding(context);
177
- if (encoding != info.ascii8bit) {
178
- return (RubyString)source.encode(context, info.utf8);
191
+ if (encoding != info.ascii8bit.get()) {
192
+ return (RubyString)source.encode(context, info.utf8.get());
179
193
  }
180
194
 
181
195
  String sniffedEncoding = sniffByteList(bl);
@@ -186,7 +200,7 @@ public class Parser extends RubyObject {
186
200
  String sniffedEncoding = sniffByteList(bl);
187
201
  if (sniffedEncoding == null) return source; // assume UTF-8
188
202
  Ruby runtime = context.getRuntime();
189
- return (RubyString)info.jsonModule.
203
+ return (RubyString)info.jsonModule.get().
190
204
  callMethod(context, "iconv",
191
205
  new IRubyObject[] {
192
206
  runtime.newString("utf-8"),
@@ -216,7 +230,7 @@ public class Parser extends RubyObject {
216
230
  private RubyString reinterpretEncoding(ThreadContext context,
217
231
  RubyString str, String sniffedEncoding) {
218
232
  RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
219
- RubyEncoding targetEncoding = info.utf8;
233
+ RubyEncoding targetEncoding = info.utf8.get();
220
234
  RubyString dup = (RubyString)str.dup();
221
235
  dup.force_encoding(context, actualEncoding);
222
236
  return (RubyString)dup.encode_bang(context, targetEncoding);
@@ -224,7 +238,7 @@ public class Parser extends RubyObject {
224
238
 
225
239
  /**
226
240
  * <code>Parser#parse()</code>
227
- *
241
+ *
228
242
  * <p>Parses the current JSON text <code>source</code> and returns the
229
243
  * complete data structure as a result.
230
244
  */
@@ -235,13 +249,32 @@ public class Parser extends RubyObject {
235
249
 
236
250
  /**
237
251
  * <code>Parser#source()</code>
238
- *
252
+ *
239
253
  * <p>Returns a copy of the current <code>source</code> string, that was
240
254
  * used to construct this Parser.
241
255
  */
242
256
  @JRubyMethod(name = "source")
243
257
  public IRubyObject source_get() {
244
- return vSource.dup();
258
+ return checkAndGetSource().dup();
259
+ }
260
+
261
+ /**
262
+ * <code>Parser#quirks_mode?()</code>
263
+ *
264
+ * <p>If set to <code>true</code>, if the parse is in quirks_mode, false
265
+ * otherwise.
266
+ */
267
+ @JRubyMethod(name = "quirks_mode?")
268
+ public IRubyObject quirks_mode_p(ThreadContext context) {
269
+ return context.getRuntime().newBoolean(quirksMode);
270
+ }
271
+
272
+ public RubyString checkAndGetSource() {
273
+ if (vSource != null) {
274
+ return vSource;
275
+ } else {
276
+ throw getRuntime().newTypeError("uninitialized instance");
277
+ }
245
278
  }
246
279
 
247
280
  /**
@@ -249,13 +282,13 @@ public class Parser extends RubyObject {
249
282
  * set to <code>nil</code> or <code>false</code>, and a String if not.
250
283
  */
251
284
  private RubyString getCreateId(ThreadContext context) {
252
- IRubyObject v = info.jsonModule.callMethod(context, "create_id");
285
+ IRubyObject v = info.jsonModule.get().callMethod(context, "create_id");
253
286
  return v.isTrue() ? v.convertToString() : null;
254
287
  }
255
288
 
256
289
  /**
257
290
  * A string parsing session.
258
- *
291
+ *
259
292
  * <p>Once a ParserSession is instantiated, the source string should not
260
293
  * change until the parsing is complete. The ParserSession object assumes
261
294
  * the source {@link RubyString} is still associated to its original
@@ -279,7 +312,7 @@ public class Parser extends RubyObject {
279
312
  private ParserSession(Parser parser, ThreadContext context) {
280
313
  this.parser = parser;
281
314
  this.context = context;
282
- this.byteList = parser.vSource.getByteList();
315
+ this.byteList = parser.checkAndGetSource().getByteList();
283
316
  this.data = byteList.unsafeBytes();
284
317
  this.decoder = new StringDecoder(context);
285
318
  }
@@ -353,7 +386,7 @@ public class Parser extends RubyObject {
353
386
  }
354
387
  }
355
388
  action parse_number {
356
- if (pe > fpc + 9 &&
389
+ if (pe > fpc + 9 - (parser.quirksMode ? 1 : 0) &&
357
390
  absSubSequence(fpc, fpc + 9).toString().equals(JSON_MINUS_INFINITY)) {
358
391
 
359
392
  if (parser.allowNaN) {
@@ -453,7 +486,7 @@ public class Parser extends RubyObject {
453
486
  fbreak;
454
487
  }
455
488
 
456
- main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9] @exit );
489
+ main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9]? @exit );
457
490
  }%%
458
491
 
459
492
  ParserResult parseInteger(int p, int pe) {
@@ -489,7 +522,7 @@ public class Parser extends RubyObject {
489
522
  main := '-'?
490
523
  ( ( ( '0' | [1-9][0-9]* ) '.' [0-9]+ ( [Ee] [+\-]?[0-9]+ )? )
491
524
  | ( ( '0' | [1-9][0-9]* ) ( [Ee] [+\-]? [0-9]+ ) ) )
492
- ( ^[0-9Ee.\-] @exit );
525
+ ( ^[0-9Ee.\-]? @exit );
493
526
  }%%
494
527
 
495
528
  ParserResult parseFloat(int p, int pe) {
@@ -567,7 +600,7 @@ public class Parser extends RubyObject {
567
600
  }
568
601
  });
569
602
  } catch (JumpException e) { }
570
- if (memoArray[1] != null) {
603
+ if (memoArray[1] != null) {
571
604
  RubyClass klass = (RubyClass) memoArray[1];
572
605
  if (klass.respondsTo("json_creatable?") &&
573
606
  klass.callMethod(context, "json_creatable?").isTrue()) {
@@ -690,15 +723,14 @@ public class Parser extends RubyObject {
690
723
  fhold;
691
724
  fbreak;
692
725
  }
726
+
727
+ pair = ignore* begin_name >parse_name ignore* name_separator
728
+ ignore* begin_value >parse_value;
729
+ next_pair = ignore* value_separator pair;
693
730
 
694
- a_pair = ignore*
695
- begin_name >parse_name
696
- ignore* name_separator ignore*
697
- begin_value >parse_value;
698
-
699
- main := begin_object
700
- (a_pair (ignore* value_separator a_pair)*)?
701
- ignore* end_object @exit;
731
+ main := (
732
+ begin_object (pair (next_pair)*)? ignore* end_object
733
+ ) @exit;
702
734
  }%%
703
735
 
704
736
  ParserResult parseObject(int p, int pe) {
@@ -730,7 +762,7 @@ public class Parser extends RubyObject {
730
762
  IRubyObject vKlassName = result.op_aref(context, parser.createId);
731
763
  if (!vKlassName.isNil()) {
732
764
  // might throw ArgumentError, we let it propagate
733
- IRubyObject klass = parser.info.jsonModule.
765
+ IRubyObject klass = parser.info.jsonModule.get().
734
766
  callMethod(context, "deep_const_get", vKlassName);
735
767
  if (klass.respondsTo("json_creatable?") &&
736
768
  klass.callMethod(context, "json_creatable?").isTrue()) {
@@ -778,7 +810,7 @@ public class Parser extends RubyObject {
778
810
  ignore*;
779
811
  }%%
780
812
 
781
- public IRubyObject parse() {
813
+ public IRubyObject parseStrict() {
782
814
  int cs = EVIL;
783
815
  int p, pe;
784
816
  IRubyObject result = null;
@@ -795,6 +827,54 @@ public class Parser extends RubyObject {
795
827
  }
796
828
  }
797
829
 
830
+ %%{
831
+ machine JSON_quirks_mode;
832
+ include JSON_common;
833
+
834
+ write data;
835
+
836
+ action parse_value {
837
+ ParserResult res = parseValue(fpc, pe);
838
+ if (res == null) {
839
+ fhold;
840
+ fbreak;
841
+ } else {
842
+ result = res.result;
843
+ fexec res.p;
844
+ }
845
+ }
846
+
847
+ main := ignore*
848
+ ( begin_value >parse_value)
849
+ ignore*;
850
+ }%%
851
+
852
+ public IRubyObject parseQuirksMode() {
853
+ int cs = EVIL;
854
+ int p, pe;
855
+ IRubyObject result = null;
856
+
857
+ %% write init;
858
+ p = byteList.begin();
859
+ pe = p + byteList.length();
860
+ %% write exec;
861
+
862
+ if (cs >= JSON_quirks_mode_first_final && p == pe) {
863
+ return result;
864
+ } else {
865
+ throw unexpectedToken(p, pe);
866
+ }
867
+ }
868
+
869
+ public IRubyObject parse() {
870
+ if (parser.quirksMode) {
871
+ return parseQuirksMode();
872
+ } else {
873
+ return parseStrict();
874
+ }
875
+
876
+ }
877
+
798
878
  /**
799
879
  * Returns a subsequence of the source ByteList, based on source
800
880
  * array byte offsets (i.e., the ByteList's own begin offset is not
@@ -813,7 +893,7 @@ public class Parser extends RubyObject {
813
893
  * @param name The constant name
814
894
  */
815
895
  private IRubyObject getConstant(String name) {
816
- return parser.info.jsonModule.getConstant(name);
896
+ return parser.info.jsonModule.get().getConstant(name);
817
897
  }
818
898
 
819
899
  private RaiseException newException(String className, String message) {
@@ -1,12 +1,13 @@
1
1
  /*
2
2
  * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
- *
3
+ *
4
4
  * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
5
  * for details.
6
6
  */
7
7
  package json.ext;
8
8
 
9
9
  import java.io.IOException;
10
+ import java.lang.ref.WeakReference;
10
11
 
11
12
  import org.jruby.Ruby;
12
13
  import org.jruby.RubyClass;
@@ -23,8 +24,8 @@ public class ParserService implements BasicLibraryService {
23
24
  runtime.getLoadService().require("json/common");
24
25
  RuntimeInfo info = RuntimeInfo.initRuntime(runtime);
25
26
 
26
- info.jsonModule = runtime.defineModule("JSON");
27
- RubyModule jsonExtModule = info.jsonModule.defineModuleUnder("Ext");
27
+ info.jsonModule = new WeakReference<RubyModule>(runtime.defineModule("JSON"));
28
+ RubyModule jsonExtModule = info.jsonModule.get().defineModuleUnder("Ext");
28
29
  RubyClass parserClass =
29
30
  jsonExtModule.defineClassUnder("Parser", runtime.getObject(),
30
31
  Parser.ALLOCATOR);
@@ -27,19 +27,21 @@ final class RuntimeInfo {
27
27
  private static Map<Ruby, RuntimeInfo> runtimes;
28
28
 
29
29
  // these fields are filled by the service loaders
30
+ // Use WeakReferences so that RuntimeInfo doesn't indirectly hold a hard reference to
31
+ // the Ruby runtime object, which would cause memory leaks in the runtimes map above.
30
32
  /** JSON */
31
- RubyModule jsonModule;
33
+ WeakReference<RubyModule> jsonModule;
32
34
  /** JSON::Ext::Generator::GeneratorMethods::String::Extend */
33
- RubyModule stringExtendModule;
35
+ WeakReference<RubyModule> stringExtendModule;
34
36
  /** JSON::Ext::Generator::State */
35
- RubyClass generatorStateClass;
37
+ WeakReference<RubyClass> generatorStateClass;
36
38
  /** JSON::SAFE_STATE_PROTOTYPE */
37
- GeneratorState safeStatePrototype;
39
+ WeakReference<GeneratorState> safeStatePrototype;
38
40
 
39
- final RubyEncoding utf8;
40
- final RubyEncoding ascii8bit;
41
+ final WeakReference<RubyEncoding> utf8;
42
+ final WeakReference<RubyEncoding> ascii8bit;
41
43
  // other encodings
42
- private final Map<String, RubyEncoding> encodings;
44
+ private final Map<String, WeakReference<RubyEncoding>> encodings;
43
45
 
44
46
  private RuntimeInfo(Ruby runtime) {
45
47
  RubyClass encodingClass = runtime.getEncoding();
@@ -49,11 +51,11 @@ final class RuntimeInfo {
49
51
  } else {
50
52
  ThreadContext context = runtime.getCurrentContext();
51
53
 
52
- utf8 = (RubyEncoding)RubyEncoding.find(context,
53
- encodingClass, runtime.newString("utf-8"));
54
- ascii8bit = (RubyEncoding)RubyEncoding.find(context,
55
- encodingClass, runtime.newString("ascii-8bit"));
56
- encodings = new HashMap<String, RubyEncoding>();
54
+ utf8 = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
55
+ encodingClass, runtime.newString("utf-8")));
56
+ ascii8bit = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
57
+ encodingClass, runtime.newString("ascii-8bit")));
58
+ encodings = new HashMap<String, WeakReference<RubyEncoding>>();
57
59
  }
58
60
  }
59
61
 
@@ -90,30 +92,30 @@ final class RuntimeInfo {
90
92
  }
91
93
 
92
94
  public boolean encodingsSupported() {
93
- return utf8 != null;
95
+ return utf8 != null && utf8.get() != null;
94
96
  }
95
97
 
96
98
  public RubyEncoding getEncoding(ThreadContext context, String name) {
97
99
  synchronized (encodings) {
98
- RubyEncoding encoding = encodings.get(name);
100
+ WeakReference<RubyEncoding> encoding = encodings.get(name);
99
101
  if (encoding == null) {
100
102
  Ruby runtime = context.getRuntime();
101
- encoding = (RubyEncoding)RubyEncoding.find(context,
102
- runtime.getEncoding(), runtime.newString(name));
103
+ encoding = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
104
+ runtime.getEncoding(), runtime.newString(name)));
103
105
  encodings.put(name, encoding);
104
106
  }
105
- return encoding;
107
+ return encoding.get();
106
108
  }
107
109
  }
108
110
 
109
111
  public GeneratorState getSafeStatePrototype(ThreadContext context) {
110
112
  if (safeStatePrototype == null) {
111
- IRubyObject value = jsonModule.getConstant("SAFE_STATE_PROTOTYPE");
113
+ IRubyObject value = jsonModule.get().getConstant("SAFE_STATE_PROTOTYPE");
112
114
  if (!(value instanceof GeneratorState)) {
113
- throw context.getRuntime().newTypeError(value, generatorStateClass);
115
+ throw context.getRuntime().newTypeError(value, generatorStateClass.get());
114
116
  }
115
- safeStatePrototype = (GeneratorState)value;
117
+ safeStatePrototype = new WeakReference<GeneratorState>((GeneratorState)value);
116
118
  }
117
- return safeStatePrototype;
119
+ return safeStatePrototype.get();
118
120
  }
119
121
  }