summaryrefslogtreecommitdiff
path: root/java/src/json
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/json')
-rw-r--r--java/src/json/ext/Generator.java10
-rw-r--r--java/src/json/ext/GeneratorMethods.java11
-rw-r--r--java/src/json/ext/GeneratorService.java7
-rw-r--r--java/src/json/ext/GeneratorState.java6
-rw-r--r--java/src/json/ext/OptionsReader.java4
-rw-r--r--java/src/json/ext/Parser.java187
-rw-r--r--java/src/json/ext/Parser.rl43
-rw-r--r--java/src/json/ext/ParserService.java5
-rw-r--r--java/src/json/ext/RuntimeInfo.java44
-rw-r--r--java/src/json/ext/Utils.java2
10 files changed, 179 insertions, 140 deletions
diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java
index 230d68f..fbc394f 100644
--- a/java/src/json/ext/Generator.java
+++ b/java/src/json/ext/Generator.java
@@ -354,11 +354,7 @@ public final class Generator {
state.decreaseDepth();
if (objectNl.length() != 0) {
buffer.append(objectNl);
- if (indent.length != 0) {
- for (int i = 0; i < state.getDepth(); i++) {
- buffer.append(indent);
- }
- }
+ buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
}
buffer.append((byte)'}');
}
@@ -380,9 +376,9 @@ public final class Generator {
RubyString src;
if (info.encodingsSupported() &&
- object.encoding(session.getContext()) != info.utf8) {
+ object.encoding(session.getContext()) != info.utf8.get()) {
src = (RubyString)object.encode(session.getContext(),
- info.utf8);
+ info.utf8.get());
} else {
src = object;
}
diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java
index 28a612d..356f2d0 100644
--- a/java/src/json/ext/GeneratorMethods.java
+++ b/java/src/json/ext/GeneratorMethods.java
@@ -6,6 +6,7 @@
*/
package json.ext;
+import java.lang.ref.WeakReference;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
@@ -45,9 +46,9 @@ class GeneratorMethods {
defineMethods(module, "String", RbString.class);
defineMethods(module, "TrueClass", RbTrue.class);
- info.stringExtendModule = module.defineModuleUnder("String")
- .defineModuleUnder("Extend");
- info.stringExtendModule.defineAnnotatedMethods(StringExtend.class);
+ info.stringExtendModule = new WeakReference<RubyModule>(module.defineModuleUnder("String")
+ .defineModuleUnder("Extend"));
+ info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class);
}
/**
@@ -140,7 +141,7 @@ class GeneratorMethods {
RubyHash result = RubyHash.newHash(runtime);
IRubyObject createId = RuntimeInfo.forRuntime(runtime)
- .jsonModule.callMethod(context, "create_id");
+ .jsonModule.get().callMethod(context, "create_id");
result.op_aset(context, createId, self.getMetaClass().to_s());
ByteList bl = self.getByteList();
@@ -158,7 +159,7 @@ class GeneratorMethods {
public static IRubyObject included(ThreadContext context,
IRubyObject vSelf, IRubyObject module) {
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- return module.callMethod(context, "extend", info.stringExtendModule);
+ return module.callMethod(context, "extend", info.stringExtendModule.get());
}
}
diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java
index b8deb22..2f3b07e 100644
--- a/java/src/json/ext/GeneratorService.java
+++ b/java/src/json/ext/GeneratorService.java
@@ -7,6 +7,7 @@
package json.ext;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import org.jruby.Ruby;
import org.jruby.RubyClass;
@@ -23,15 +24,15 @@ public class GeneratorService implements BasicLibraryService {
runtime.getLoadService().require("json/common");
RuntimeInfo info = RuntimeInfo.initRuntime(runtime);
- info.jsonModule = runtime.defineModule("JSON");
- RubyModule jsonExtModule = info.jsonModule.defineModuleUnder("Ext");
+ info.jsonModule = new WeakReference<RubyModule>(runtime.defineModule("JSON"));
+ RubyModule jsonExtModule = info.jsonModule.get().defineModuleUnder("Ext");
RubyModule generatorModule = jsonExtModule.defineModuleUnder("Generator");
RubyClass stateClass =
generatorModule.defineClassUnder("State", runtime.getObject(),
GeneratorState.ALLOCATOR);
stateClass.defineAnnotatedMethods(GeneratorState.class);
- info.generatorStateClass = stateClass;
+ info.generatorStateClass = new WeakReference<RubyClass>(stateClass);
RubyModule generatorMethods =
generatorModule.defineModuleUnder("GeneratorMethods");
diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java
index dc99000..f04eda2 100644
--- a/java/src/json/ext/GeneratorState.java
+++ b/java/src/json/ext/GeneratorState.java
@@ -118,7 +118,7 @@ public class GeneratorState extends RubyObject {
static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
IRubyObject opts) {
- RubyClass klass = info.generatorStateClass;
+ RubyClass klass = info.generatorStateClass.get();
if (opts != null) {
// if the given parameter is a Generator::State, return itself
if (klass.isInstance(opts)) return (GeneratorState)opts;
@@ -382,8 +382,8 @@ public class GeneratorState extends RubyObject {
private ByteList prepareByteList(ThreadContext context, IRubyObject value) {
RubyString str = value.convertToString();
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- if (info.encodingsSupported() && str.encoding(context) != info.utf8) {
- str = (RubyString)str.encode(context, info.utf8);
+ if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
+ str = (RubyString)str.encode(context, info.utf8.get());
}
return str.getByteList().dup();
}
diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java
index 018ace4..c9c9c94 100644
--- a/java/src/json/ext/OptionsReader.java
+++ b/java/src/json/ext/OptionsReader.java
@@ -84,8 +84,8 @@ final class OptionsReader {
RubyString str = value.convertToString();
RuntimeInfo info = getRuntimeInfo();
- if (info.encodingsSupported() && str.encoding(context) != info.utf8) {
- str = (RubyString)str.encode(context, info.utf8);
+ if (info.encodingsSupported() && str.encoding(context) != info.utf8.get()) {
+ str = (RubyString)str.encode(context, info.utf8.get());
}
return str;
}
diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java
index c92600e..cea42d4 100644
--- a/java/src/json/ext/Parser.java
+++ b/java/src/json/ext/Parser.java
@@ -144,7 +144,10 @@ public class Parser extends RubyObject {
@JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.getRuntime();
+ if (this.vSource != null) {
+ throw runtime.newTypeError("already initialized instance");
+ }
RubyString source = convertEncoding(context, args[0].convertToString());
OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
@@ -176,8 +179,8 @@ public class Parser extends RubyObject {
if (info.encodingsSupported()) {
RubyEncoding encoding = (RubyEncoding)source.encoding(context);
- if (encoding != info.ascii8bit) {
- return (RubyString)source.encode(context, info.utf8);
+ if (encoding != info.ascii8bit.get()) {
+ return (RubyString)source.encode(context, info.utf8.get());
}
String sniffedEncoding = sniffByteList(bl);
@@ -188,7 +191,7 @@ public class Parser extends RubyObject {
String sniffedEncoding = sniffByteList(bl);
if (sniffedEncoding == null) return source; // assume UTF-8
Ruby runtime = context.getRuntime();
- return (RubyString)info.jsonModule.
+ return (RubyString)info.jsonModule.get().
callMethod(context, "iconv",
new IRubyObject[] {
runtime.newString("utf-8"),
@@ -218,7 +221,7 @@ public class Parser extends RubyObject {
private RubyString reinterpretEncoding(ThreadContext context,
RubyString str, String sniffedEncoding) {
RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
- RubyEncoding targetEncoding = info.utf8;
+ RubyEncoding targetEncoding = info.utf8.get();
RubyString dup = (RubyString)str.dup();
dup.force_encoding(context, actualEncoding);
return (RubyString)dup.encode_bang(context, targetEncoding);
@@ -243,7 +246,15 @@ public class Parser extends RubyObject {
*/
@JRubyMethod(name = "source")
public IRubyObject source_get() {
- return vSource.dup();
+ return checkAndGetSource().dup();
+ }
+
+ public RubyString checkAndGetSource() {
+ if (vSource != null) {
+ return vSource;
+ } else {
+ throw getRuntime().newTypeError("uninitialized instance");
+ }
}
/**
@@ -251,7 +262,7 @@ public class Parser extends RubyObject {
* set to <code>nil</code> or <code>false</code>, and a String if not.
*/
private RubyString getCreateId(ThreadContext context) {
- IRubyObject v = info.jsonModule.callMethod(context, "create_id");
+ IRubyObject v = info.jsonModule.get().callMethod(context, "create_id");
return v.isTrue() ? v.convertToString() : null;
}
@@ -281,7 +292,7 @@ public class Parser extends RubyObject {
private ParserSession(Parser parser, ThreadContext context) {
this.parser = parser;
this.context = context;
- this.byteList = parser.vSource.getByteList();
+ this.byteList = parser.checkAndGetSource().getByteList();
this.data = byteList.unsafeBytes();
this.decoder = new StringDecoder(context);
}
@@ -298,11 +309,11 @@ public class Parser extends RubyObject {
}
-// line 324 "Parser.rl"
+// line 335 "Parser.rl"
-// line 306 "Parser.java"
+// line 317 "Parser.java"
private static byte[] init__JSON_value_actions_0()
{
return new byte [] {
@@ -416,7 +427,7 @@ static final int JSON_value_error = 0;
static final int JSON_value_en_main = 1;
-// line 430 "Parser.rl"
+// line 441 "Parser.rl"
ParserResult parseValue(int p, int pe) {
@@ -424,14 +435,14 @@ static final int JSON_value_en_main = 1;
IRubyObject result = null;
-// line 428 "Parser.java"
+// line 439 "Parser.java"
{
cs = JSON_value_start;
}
-// line 437 "Parser.rl"
+// line 448 "Parser.rl"
-// line 435 "Parser.java"
+// line 446 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -457,13 +468,13 @@ case 1:
while ( _nacts-- > 0 ) {
switch ( _JSON_value_actions[_acts++] ) {
case 9:
-// line 415 "Parser.rl"
+// line 426 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 467 "Parser.java"
+// line 478 "Parser.java"
}
}
@@ -526,25 +537,25 @@ case 1:
switch ( _JSON_value_actions[_acts++] )
{
case 0:
-// line 332 "Parser.rl"
+// line 343 "Parser.rl"
{
result = getRuntime().getNil();
}
break;
case 1:
-// line 335 "Parser.rl"
+// line 346 "Parser.rl"
{
result = getRuntime().getFalse();
}
break;
case 2:
-// line 338 "Parser.rl"
+// line 349 "Parser.rl"
{
result = getRuntime().getTrue();
}
break;
case 3:
-// line 341 "Parser.rl"
+// line 352 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_NAN);
@@ -554,7 +565,7 @@ case 1:
}
break;
case 4:
-// line 348 "Parser.rl"
+// line 359 "Parser.rl"
{
if (parser.allowNaN) {
result = getConstant(CONST_INFINITY);
@@ -564,7 +575,7 @@ case 1:
}
break;
case 5:
-// line 355 "Parser.rl"
+// line 366 "Parser.rl"
{
if (pe > p + 9 &&
absSubSequence(p, p + 9).toString().equals(JSON_MINUS_INFINITY)) {
@@ -593,7 +604,7 @@ case 1:
}
break;
case 6:
-// line 381 "Parser.rl"
+// line 392 "Parser.rl"
{
ParserResult res = parseString(p, pe);
if (res == null) {
@@ -606,7 +617,7 @@ case 1:
}
break;
case 7:
-// line 391 "Parser.rl"
+// line 402 "Parser.rl"
{
currentNesting++;
ParserResult res = parseArray(p, pe);
@@ -621,7 +632,7 @@ case 1:
}
break;
case 8:
-// line 403 "Parser.rl"
+// line 414 "Parser.rl"
{
currentNesting++;
ParserResult res = parseObject(p, pe);
@@ -635,7 +646,7 @@ case 1:
}
}
break;
-// line 639 "Parser.java"
+// line 650 "Parser.java"
}
}
}
@@ -655,7 +666,7 @@ case 5:
break; }
}
-// line 438 "Parser.rl"
+// line 449 "Parser.rl"
if (cs >= JSON_value_first_final && result != null) {
return new ParserResult(result, p);
@@ -665,7 +676,7 @@ case 5:
}
-// line 669 "Parser.java"
+// line 680 "Parser.java"
private static byte[] init__JSON_integer_actions_0()
{
return new byte [] {
@@ -764,22 +775,22 @@ static final int JSON_integer_error = 0;
static final int JSON_integer_en_main = 1;
-// line 457 "Parser.rl"
+// line 468 "Parser.rl"
ParserResult parseInteger(int p, int pe) {
int cs = EVIL;
-// line 775 "Parser.java"
+// line 786 "Parser.java"
{
cs = JSON_integer_start;
}
-// line 463 "Parser.rl"
+// line 474 "Parser.rl"
int memo = p;
-// line 783 "Parser.java"
+// line 794 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -860,13 +871,13 @@ case 1:
switch ( _JSON_integer_actions[_acts++] )
{
case 0:
-// line 451 "Parser.rl"
+// line 462 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 870 "Parser.java"
+// line 881 "Parser.java"
}
}
}
@@ -886,7 +897,7 @@ case 5:
break; }
}
-// line 465 "Parser.rl"
+// line 476 "Parser.rl"
if (cs < JSON_integer_first_final) {
return null;
@@ -901,7 +912,7 @@ case 5:
}
-// line 905 "Parser.java"
+// line 916 "Parser.java"
private static byte[] init__JSON_float_actions_0()
{
return new byte [] {
@@ -1003,22 +1014,22 @@ static final int JSON_float_error = 0;
static final int JSON_float_en_main = 1;
-// line 493 "Parser.rl"
+// line 504 "Parser.rl"
ParserResult parseFloat(int p, int pe) {
int cs = EVIL;
-// line 1014 "Parser.java"
+// line 1025 "Parser.java"
{
cs = JSON_float_start;
}
-// line 499 "Parser.rl"
+// line 510 "Parser.rl"
int memo = p;
-// line 1022 "Parser.java"
+// line 1033 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1099,13 +1110,13 @@ case 1:
switch ( _JSON_float_actions[_acts++] )
{
case 0:
-// line 484 "Parser.rl"
+// line 495 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1109 "Parser.java"
+// line 1120 "Parser.java"
}
}
}
@@ -1125,7 +1136,7 @@ case 5:
break; }
}
-// line 501 "Parser.rl"
+// line 512 "Parser.rl"
if (cs < JSON_float_first_final) {
return null;
@@ -1140,7 +1151,7 @@ case 5:
}
-// line 1144 "Parser.java"
+// line 1155 "Parser.java"
private static byte[] init__JSON_string_actions_0()
{
return new byte [] {
@@ -1242,7 +1253,7 @@ static final int JSON_string_error = 0;
static final int JSON_string_en_main = 1;
-// line 545 "Parser.rl"
+// line 556 "Parser.rl"
ParserResult parseString(int p, int pe) {
@@ -1250,15 +1261,15 @@ static final int JSON_string_en_main = 1;
IRubyObject result = null;
-// line 1254 "Parser.java"
+// line 1265 "Parser.java"
{
cs = JSON_string_start;
}
-// line 552 "Parser.rl"
+// line 563 "Parser.rl"
int memo = p;
-// line 1262 "Parser.java"
+// line 1273 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1339,7 +1350,7 @@ case 1:
switch ( _JSON_string_actions[_acts++] )
{
case 0:
-// line 520 "Parser.rl"
+// line 531 "Parser.rl"
{
int offset = byteList.begin();
ByteList decoded = decoder.decode(byteList, memo + 1 - offset,
@@ -1354,13 +1365,13 @@ case 1:
}
break;
case 1:
-// line 533 "Parser.rl"
+// line 544 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1364 "Parser.java"
+// line 1375 "Parser.java"
}
}
}
@@ -1380,7 +1391,7 @@ case 5:
break; }
}
-// line 554 "Parser.rl"
+// line 565 "Parser.rl"
if (parser.createAdditions) {
RubyHash match_string = parser.match_string;
@@ -1415,7 +1426,7 @@ case 5:
}
-// line 1419 "Parser.java"
+// line 1430 "Parser.java"
private static byte[] init__JSON_array_actions_0()
{
return new byte [] {
@@ -1528,7 +1539,7 @@ static final int JSON_array_error = 0;
static final int JSON_array_en_main = 1;
-// line 620 "Parser.rl"
+// line 635 "Parser.rl"
ParserResult parseArray(int p, int pe) {
@@ -1546,14 +1557,14 @@ static final int JSON_array_en_main = 1;
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
-// line 1550 "Parser.java"
+// line 1561 "Parser.java"
{
cs = JSON_array_start;
}
-// line 637 "Parser.rl"
+// line 652 "Parser.rl"
-// line 1557 "Parser.java"
+// line 1568 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1634,26 +1645,30 @@ case 1:
switch ( _JSON_array_actions[_acts++] )
{
case 0:
-// line 593 "Parser.rl"
+// line 604 "Parser.rl"
{
ParserResult res = parseValue(p, pe);
if (res == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
} else {
- result.append(res.result);
+ if (!parser.arrayClass.getName().equals("Array")) {
+ result.callMethod(context, "<<", res.result);
+ } else {
+ result.append(res.result);
+ }
{p = (( res.p))-1;}
}
}
break;
case 1:
-// line 604 "Parser.rl"
+// line 619 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1657 "Parser.java"
+// line 1672 "Parser.java"
}
}
}
@@ -1673,7 +1688,7 @@ case 5:
break; }
}
-// line 638 "Parser.rl"
+// line 653 "Parser.rl"
if (cs >= JSON_array_first_final) {
return new ParserResult(result, p + 1);
@@ -1683,7 +1698,7 @@ case 5:
}
-// line 1687 "Parser.java"
+// line 1702 "Parser.java"
private static byte[] init__JSON_object_actions_0()
{
return new byte [] {
@@ -1806,7 +1821,7 @@ static final int JSON_object_error = 0;
static final int JSON_object_en_main = 1;
-// line 694 "Parser.rl"
+// line 713 "Parser.rl"
ParserResult parseObject(int p, int pe) {
@@ -1825,14 +1840,14 @@ static final int JSON_object_en_main = 1;
IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
-// line 1829 "Parser.java"
+// line 1844 "Parser.java"
{
cs = JSON_object_start;
}
-// line 712 "Parser.rl"
+// line 731 "Parser.rl"
-// line 1836 "Parser.java"
+// line 1851 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -1913,20 +1928,24 @@ case 1:
switch ( _JSON_object_actions[_acts++] )
{
case 0:
-// line 652 "Parser.rl"
+// line 667 "Parser.rl"
{
ParserResult res = parseValue(p, pe);
if (res == null) {
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
} else {
- result.op_aset(context, lastName, res.result);
+ if (!parser.objectClass.getName().equals("Hash")) {
+ result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
+ } else {
+ result.op_aset(context, lastName, res.result);
+ }
{p = (( res.p))-1;}
}
}
break;
case 1:
-// line 663 "Parser.rl"
+// line 682 "Parser.rl"
{
ParserResult res = parseString(p, pe);
if (res == null) {
@@ -1946,13 +1965,13 @@ case 1:
}
break;
case 2:
-// line 681 "Parser.rl"
+// line 700 "Parser.rl"
{
p--;
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 1956 "Parser.java"
+// line 1975 "Parser.java"
}
}
}
@@ -1972,7 +1991,7 @@ case 5:
break; }
}
-// line 713 "Parser.rl"
+// line 732 "Parser.rl"
if (cs < JSON_object_first_final) {
return null;
@@ -1985,7 +2004,7 @@ case 5:
IRubyObject vKlassName = result.op_aref(context, parser.createId);
if (!vKlassName.isNil()) {
// might throw ArgumentError, we let it propagate
- IRubyObject klass = parser.info.jsonModule.
+ IRubyObject klass = parser.info.jsonModule.get().
callMethod(context, "deep_const_get", vKlassName);
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
@@ -1998,7 +2017,7 @@ case 5:
}
-// line 2002 "Parser.java"
+// line 2021 "Parser.java"
private static byte[] init__JSON_actions_0()
{
return new byte [] {
@@ -2102,7 +2121,7 @@ static final int JSON_error = 0;
static final int JSON_en_main = 1;
-// line 771 "Parser.rl"
+// line 790 "Parser.rl"
public IRubyObject parse() {
@@ -2111,16 +2130,16 @@ static final int JSON_en_main = 1;
IRubyObject result = null;
-// line 2115 "Parser.java"
+// line 2134 "Parser.java"
{
cs = JSON_start;
}
-// line 779 "Parser.rl"
+// line 798 "Parser.rl"
p = byteList.begin();
pe = p + byteList.length();
-// line 2124 "Parser.java"
+// line 2143 "Parser.java"
{
int _klen;
int _trans = 0;
@@ -2201,7 +2220,7 @@ case 1:
switch ( _JSON_actions[_acts++] )
{
case 0:
-// line 743 "Parser.rl"
+// line 762 "Parser.rl"
{
currentNesting = 1;
ParserResult res = parseObject(p, pe);
@@ -2215,7 +2234,7 @@ case 1:
}
break;
case 1:
-// line 755 "Parser.rl"
+// line 774 "Parser.rl"
{
currentNesting = 1;
ParserResult res = parseArray(p, pe);
@@ -2228,7 +2247,7 @@ case 1:
}
}
break;
-// line 2232 "Parser.java"
+// line 2251 "Parser.java"
}
}
}
@@ -2248,7 +2267,7 @@ case 5:
break; }
}
-// line 782 "Parser.rl"
+// line 801 "Parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -2275,7 +2294,7 @@ case 5:
* @param name The constant name
*/
private IRubyObject getConstant(String name) {
- return parser.info.jsonModule.getConstant(name);
+ return parser.info.jsonModule.get().getConstant(name);
}
private RaiseException newException(String className, String message) {
diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl
index e576b97..779d3f3 100644
--- a/java/src/json/ext/Parser.rl
+++ b/java/src/json/ext/Parser.rl
@@ -142,7 +142,10 @@ public class Parser extends RubyObject {
@JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
+ Ruby runtime = context.getRuntime();
+ if (this.vSource != null) {
+ throw runtime.newTypeError("already initialized instance");
+ }
RubyString source = convertEncoding(context, args[0].convertToString());
OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null);
@@ -174,8 +177,8 @@ public class Parser extends RubyObject {
if (info.encodingsSupported()) {
RubyEncoding encoding = (RubyEncoding)source.encoding(context);
- if (encoding != info.ascii8bit) {
- return (RubyString)source.encode(context, info.utf8);
+ if (encoding != info.ascii8bit.get()) {
+ return (RubyString)source.encode(context, info.utf8.get());
}
String sniffedEncoding = sniffByteList(bl);
@@ -186,7 +189,7 @@ public class Parser extends RubyObject {
String sniffedEncoding = sniffByteList(bl);
if (sniffedEncoding == null) return source; // assume UTF-8
Ruby runtime = context.getRuntime();
- return (RubyString)info.jsonModule.
+ return (RubyString)info.jsonModule.get().
callMethod(context, "iconv",
new IRubyObject[] {
runtime.newString("utf-8"),
@@ -216,7 +219,7 @@ public class Parser extends RubyObject {
private RubyString reinterpretEncoding(ThreadContext context,
RubyString str, String sniffedEncoding) {
RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding);
- RubyEncoding targetEncoding = info.utf8;
+ RubyEncoding targetEncoding = info.utf8.get();
RubyString dup = (RubyString)str.dup();
dup.force_encoding(context, actualEncoding);
return (RubyString)dup.encode_bang(context, targetEncoding);
@@ -241,7 +244,15 @@ public class Parser extends RubyObject {
*/
@JRubyMethod(name = "source")
public IRubyObject source_get() {
- return vSource.dup();
+ return checkAndGetSource().dup();
+ }
+
+ public RubyString checkAndGetSource() {
+ if (vSource != null) {
+ return vSource;
+ } else {
+ throw getRuntime().newTypeError("uninitialized instance");
+ }
}
/**
@@ -249,7 +260,7 @@ public class Parser extends RubyObject {
* set to <code>nil</code> or <code>false</code>, and a String if not.
*/
private RubyString getCreateId(ThreadContext context) {
- IRubyObject v = info.jsonModule.callMethod(context, "create_id");
+ IRubyObject v = info.jsonModule.get().callMethod(context, "create_id");
return v.isTrue() ? v.convertToString() : null;
}
@@ -279,7 +290,7 @@ public class Parser extends RubyObject {
private ParserSession(Parser parser, ThreadContext context) {
this.parser = parser;
this.context = context;
- this.byteList = parser.vSource.getByteList();
+ this.byteList = parser.checkAndGetSource().getByteList();
this.data = byteList.unsafeBytes();
this.decoder = new StringDecoder(context);
}
@@ -596,7 +607,11 @@ public class Parser extends RubyObject {
fhold;
fbreak;
} else {
- result.append(res.result);
+ if (!parser.arrayClass.getName().equals("Array")) {
+ result.callMethod(context, "<<", res.result);
+ } else {
+ result.append(res.result);
+ }
fexec res.p;
}
}
@@ -655,7 +670,11 @@ public class Parser extends RubyObject {
fhold;
fbreak;
} else {
- result.op_aset(context, lastName, res.result);
+ if (!parser.objectClass.getName().equals("Hash")) {
+ result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result });
+ } else {
+ result.op_aset(context, lastName, res.result);
+ }
fexec res.p;
}
}
@@ -722,7 +741,7 @@ public class Parser extends RubyObject {
IRubyObject vKlassName = result.op_aref(context, parser.createId);
if (!vKlassName.isNil()) {
// might throw ArgumentError, we let it propagate
- IRubyObject klass = parser.info.jsonModule.
+ IRubyObject klass = parser.info.jsonModule.get().
callMethod(context, "deep_const_get", vKlassName);
if (klass.respondsTo("json_creatable?") &&
klass.callMethod(context, "json_creatable?").isTrue()) {
@@ -805,7 +824,7 @@ public class Parser extends RubyObject {
* @param name The constant name
*/
private IRubyObject getConstant(String name) {
- return parser.info.jsonModule.getConstant(name);
+ return parser.info.jsonModule.get().getConstant(name);
}
private RaiseException newException(String className, String message) {
diff --git a/java/src/json/ext/ParserService.java b/java/src/json/ext/ParserService.java
index e0805a7..f2ecae1 100644
--- a/java/src/json/ext/ParserService.java
+++ b/java/src/json/ext/ParserService.java
@@ -7,6 +7,7 @@
package json.ext;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import org.jruby.Ruby;
import org.jruby.RubyClass;
@@ -23,8 +24,8 @@ public class ParserService implements BasicLibraryService {
runtime.getLoadService().require("json/common");
RuntimeInfo info = RuntimeInfo.initRuntime(runtime);
- info.jsonModule = runtime.defineModule("JSON");
- RubyModule jsonExtModule = info.jsonModule.defineModuleUnder("Ext");
+ info.jsonModule = new WeakReference<RubyModule>(runtime.defineModule("JSON"));
+ RubyModule jsonExtModule = info.jsonModule.get().defineModuleUnder("Ext");
RubyClass parserClass =
jsonExtModule.defineClassUnder("Parser", runtime.getObject(),
Parser.ALLOCATOR);
diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java
index f446afe..5de5740 100644
--- a/java/src/json/ext/RuntimeInfo.java
+++ b/java/src/json/ext/RuntimeInfo.java
@@ -27,19 +27,21 @@ final class RuntimeInfo {
private static Map<Ruby, RuntimeInfo> runtimes;
// these fields are filled by the service loaders
+ // Use WeakReferences so that RuntimeInfo doesn't indirectly hold a hard reference to
+ // the Ruby runtime object, which would cause memory leaks in the runtimes map above.
/** JSON */
- RubyModule jsonModule;
+ WeakReference<RubyModule> jsonModule;
/** JSON::Ext::Generator::GeneratorMethods::String::Extend */
- RubyModule stringExtendModule;
+ WeakReference<RubyModule> stringExtendModule;
/** JSON::Ext::Generator::State */
- RubyClass generatorStateClass;
+ WeakReference<RubyClass> generatorStateClass;
/** JSON::SAFE_STATE_PROTOTYPE */
- GeneratorState safeStatePrototype;
+ WeakReference<GeneratorState> safeStatePrototype;
- final RubyEncoding utf8;
- final RubyEncoding ascii8bit;
+ final WeakReference<RubyEncoding> utf8;
+ final WeakReference<RubyEncoding> ascii8bit;
// other encodings
- private final Map<String, RubyEncoding> encodings;
+ private final Map<String, WeakReference<RubyEncoding>> encodings;
private RuntimeInfo(Ruby runtime) {
RubyClass encodingClass = runtime.getEncoding();
@@ -49,11 +51,11 @@ final class RuntimeInfo {
} else {
ThreadContext context = runtime.getCurrentContext();
- utf8 = (RubyEncoding)RubyEncoding.find(context,
- encodingClass, runtime.newString("utf-8"));
- ascii8bit = (RubyEncoding)RubyEncoding.find(context,
- encodingClass, runtime.newString("ascii-8bit"));
- encodings = new HashMap<String, RubyEncoding>();
+ utf8 = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
+ encodingClass, runtime.newString("utf-8")));
+ ascii8bit = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
+ encodingClass, runtime.newString("ascii-8bit")));
+ encodings = new HashMap<String, WeakReference<RubyEncoding>>();
}
}
@@ -90,30 +92,30 @@ final class RuntimeInfo {
}
public boolean encodingsSupported() {
- return utf8 != null;
+ return utf8 != null && utf8.get() != null;
}
public RubyEncoding getEncoding(ThreadContext context, String name) {
synchronized (encodings) {
- RubyEncoding encoding = encodings.get(name);
+ WeakReference<RubyEncoding> encoding = encodings.get(name);
if (encoding == null) {
Ruby runtime = context.getRuntime();
- encoding = (RubyEncoding)RubyEncoding.find(context,
- runtime.getEncoding(), runtime.newString(name));
+ encoding = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
+ runtime.getEncoding(), runtime.newString(name)));
encodings.put(name, encoding);
}
- return encoding;
+ return encoding.get();
}
}
public GeneratorState getSafeStatePrototype(ThreadContext context) {
if (safeStatePrototype == null) {
- IRubyObject value = jsonModule.getConstant("SAFE_STATE_PROTOTYPE");
+ IRubyObject value = jsonModule.get().getConstant("SAFE_STATE_PROTOTYPE");
if (!(value instanceof GeneratorState)) {
- throw context.getRuntime().newTypeError(value, generatorStateClass);
+ throw context.getRuntime().newTypeError(value, generatorStateClass.get());
}
- safeStatePrototype = (GeneratorState)value;
+ safeStatePrototype = new WeakReference<GeneratorState>((GeneratorState)value);
}
- return safeStatePrototype;
+ return safeStatePrototype.get();
}
}
diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java
index 7a1dfee..f52ac8d 100644
--- a/java/src/json/ext/Utils.java
+++ b/java/src/json/ext/Utils.java
@@ -66,7 +66,7 @@ final class Utils {
static RaiseException newException(ThreadContext context,
String className, RubyString message) {
RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
- RubyClass klazz = info.jsonModule.getClass(className);
+ RubyClass klazz = info.jsonModule.get().getClass(className);
RubyException excptn =
(RubyException)klazz.newInstance(context,
new IRubyObject[] {message}, Block.NULL_BLOCK);