json_pure 1.5.3 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }