From 76ada33cbb42f7b2f064fe2820d5760b5658d5b1 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 07:59:46 -0600 Subject: [PATCH 01/15] Flush buffer once generation is complete --- java/src/json/ext/Generator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 65c30ffa..2454f10c 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -194,6 +194,7 @@ RubyString generateNew(Session session, T object) { void generateToBuffer(Session session, T object, OutputStream buffer) { try { generate(session, object, buffer); + buffer.flush(); } catch (IOException ioe) { throw session.getRuntime().newIOErrorFromException(ioe); } From 351e11d40fe98aa93eb91a86448a2252e9c75fc7 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 08:11:50 -0600 Subject: [PATCH 02/15] Buffer output to an IO This improves performance by having fewer dynamic writes. --- java/src/json/ext/Generator.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 2454f10c..c909a110 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -24,11 +24,15 @@ import org.jruby.exceptions.RaiseException; import org.jruby.util.IOOutputStream; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; public final class Generator { + + private static final int IO_BUFFER_SIZE = 8192; + private Generator() { throw new RuntimeException(); } @@ -67,7 +71,8 @@ private Generator() { return handler.generateNew(session, object); } - handler.generateToBuffer(session, object, new IOOutputStream(io)); + BufferedOutputStream buffer = new BufferedOutputStream(new IOOutputStream(io), IO_BUFFER_SIZE); + handler.generateToBuffer(session, object, buffer); return io; } From adb8e56b5253361ecbc83b52436f98a3f2fcea8b Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 08:33:53 -0600 Subject: [PATCH 03/15] Split generator to_json methods by arity --- java/src/json/ext/Generator.java | 21 ++-- java/src/json/ext/GeneratorMethods.java | 142 ++++++++++++++---------- 2 files changed, 100 insertions(+), 63 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index c909a110..72918cd5 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -40,10 +40,13 @@ private Generator() { /** * Encodes the given object as a JSON string, using the given handler. */ - static RubyString - generateJson(ThreadContext context, T object, - Handler handler, IRubyObject[] args) { - Session session = new Session(context, args.length > 0 ? args[0] : null); + static RubyString generateJson(ThreadContext context, T object, Handler handler) { + Session session = new Session(context, null); + return session.infect(handler.generateNew(session, object)); + } + + static RubyString generateJson(ThreadContext context, T object, Handler handler, IRubyObject arg0) { + Session session = new Session(context, arg0); return session.infect(handler.generateNew(session, object)); } @@ -51,10 +54,14 @@ private Generator() { * Encodes the given object as a JSON string, detecting the appropriate handler * for the given object. */ - static RubyString - generateJson(ThreadContext context, T object, IRubyObject[] args) { + static RubyString generateJson(ThreadContext context, T object) { + Handler handler = getHandlerFor(context.runtime, object); + return generateJson(context, object, handler); + } + + static RubyString generateJson(ThreadContext context, T object, IRubyObject arg0) { Handler handler = getHandlerFor(context.runtime, object); - return generateJson(context, object, handler, args); + return generateJson(context, object, handler, arg0); } /** diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java index bde7a18d..ec44f4b3 100644 --- a/java/src/json/ext/GeneratorMethods.java +++ b/java/src/json/ext/GeneratorMethods.java @@ -62,48 +62,63 @@ private static void defineMethods(RubyModule parentModule, submodule.defineAnnotatedMethods(klass); } - public static class RbHash { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyHash)vSelf, - Generator.HASH_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyHash)vSelf, Generator.HASH_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyHash)vSelf, Generator.HASH_HANDLER, arg0); } } public static class RbArray { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyArray)vSelf, - Generator.ARRAY_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER, arg0); } } public static class RbInteger { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, vSelf, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, vSelf); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, vSelf, arg0); } } public static class RbFloat { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyFloat)vSelf, - Generator.FLOAT_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyFloat)vSelf, Generator.FLOAT_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyFloat)vSelf, Generator.FLOAT_HANDLER, arg0); } } public static class RbString { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyString)vSelf, - Generator.STRING_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyString)vSelf, Generator.STRING_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyString)vSelf, Generator.STRING_HANDLER, arg0); } /** @@ -112,12 +127,16 @@ public static IRubyObject to_json(ThreadContext context, *

This method creates a JSON text from the result of a call to * {@link #to_json_raw_object} of this String. */ - @JRubyMethod(rest=true) - public static IRubyObject to_json_raw(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { + @JRubyMethod + public static IRubyObject to_json_raw(ThreadContext context, IRubyObject vSelf) { RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf)); - return Generator.generateJson(context, obj, - Generator.HASH_HANDLER, args); + return Generator.generateJson(context, obj, Generator.HASH_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json_raw(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + RubyHash obj = toJsonRawObject(context, Utils.ensureString(vSelf)); + return Generator.generateJson(context, obj, Generator.HASH_HANDLER, arg0); } /** @@ -128,9 +147,8 @@ public static IRubyObject to_json_raw(ThreadContext context, * method should be used if you want to convert raw strings to JSON * instead of UTF-8 strings, e.g. binary data. */ - @JRubyMethod(rest=true) - public static IRubyObject to_json_raw_object(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { + @JRubyMethod + public static IRubyObject to_json_raw_object(ThreadContext context, IRubyObject vSelf) { return toJsonRawObject(context, Utils.ensureString(vSelf)); } @@ -154,9 +172,8 @@ private static RubyHash toJsonRawObject(ThreadContext context, return result; } - @JRubyMethod(required=1, module=true) - public static IRubyObject included(ThreadContext context, - IRubyObject vSelf, IRubyObject module) { + @JRubyMethod(module=true) + public static IRubyObject included(ThreadContext context, IRubyObject vSelf, IRubyObject module) { RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); return module.callMethod(context, "extend", info.stringExtendModule.get()); } @@ -170,7 +187,7 @@ public static class StringExtend { * array for the key "raw"). The Ruby String can be created by this * module method. */ - @JRubyMethod(required=1) + @JRubyMethod public static IRubyObject json_create(ThreadContext context, IRubyObject vSelf, IRubyObject vHash) { Ruby runtime = context.getRuntime(); @@ -195,37 +212,50 @@ public static IRubyObject json_create(ThreadContext context, } public static class RbTrue { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyBoolean)vSelf, - Generator.TRUE_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.TRUE_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.TRUE_HANDLER, arg0); } } public static class RbFalse { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, (RubyBoolean)vSelf, - Generator.FALSE_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.FALSE_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, (RubyBoolean)vSelf, Generator.FALSE_HANDLER, arg0); } } public static class RbNil { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject vSelf, IRubyObject[] args) { - return Generator.generateJson(context, vSelf, - Generator.NIL_HANDLER, args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { + return Generator.generateJson(context, vSelf, Generator.NIL_HANDLER); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { + return Generator.generateJson(context, vSelf, Generator.NIL_HANDLER, arg0); } } public static class RbObject { - @JRubyMethod(rest=true) - public static IRubyObject to_json(ThreadContext context, - IRubyObject self, IRubyObject[] args) { - return RbString.to_json(context, self.asString(), args); + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject self) { + return RbString.to_json(context, self.asString()); + } + + @JRubyMethod + public static IRubyObject to_json(ThreadContext context, IRubyObject self, IRubyObject arg0) { + return RbString.to_json(context, self.asString(), arg0); } } } From 420bb5049c0d77ba4b8f62424c1777e6d3281de1 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 08:34:07 -0600 Subject: [PATCH 04/15] Hard reference the JSON module in case of GC --- java/src/json/ext/GeneratorService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java index 1500c412..7d3f86e5 100644 --- a/java/src/json/ext/GeneratorService.java +++ b/java/src/json/ext/GeneratorService.java @@ -23,7 +23,8 @@ public boolean basicLoad(Ruby runtime) throws IOException { runtime.getLoadService().require("json/common"); RuntimeInfo info = RuntimeInfo.initRuntime(runtime); - info.jsonModule = new WeakReference(runtime.defineModule("JSON")); + RubyModule jsonModule = runtime.defineModule("JSON"); + info.jsonModule = new WeakReference(jsonModule); RubyModule jsonExtModule = info.jsonModule.get().defineModuleUnder("Ext"); RubyModule generatorModule = jsonExtModule.defineModuleUnder("Generator"); From d01c9a24296cd154460962977abf1f63c913f22c Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 08:58:01 -0600 Subject: [PATCH 05/15] Reduce alloc for numeric and keyword handlers --- java/src/json/ext/Generator.java | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 72918cd5..d4de15c2 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -22,11 +22,13 @@ import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; import org.jruby.exceptions.RaiseException; +import org.jruby.util.ConvertBytes; import org.jruby.util.IOOutputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.math.BigInteger; import java.nio.charset.StandardCharsets; public final class Generator { @@ -220,25 +222,25 @@ void generateToBuffer(Session session, T object, OutputStream buffer) { */ private static class KeywordHandler extends Handler { - private String keyword; + private byte[] keyword; private KeywordHandler(String keyword) { - this.keyword = keyword; + this.keyword = keyword.getBytes(StandardCharsets.UTF_8); } @Override int guessSize(Session session, T object) { - return keyword.length(); + return keyword.length; } @Override RubyString generateNew(Session session, T object) { - return RubyString.newString(session.getRuntime(), keyword); + return RubyString.newStringShared(session.getRuntime(), keyword); } @Override void generate(Session session, T object, OutputStream buffer) throws IOException { - buffer.write(keyword.getBytes(StandardCharsets.UTF_8)); + buffer.write(keyword); } } @@ -249,11 +251,8 @@ void generate(Session session, T object, OutputStream buffer) throws IOException new Handler() { @Override void generate(Session session, RubyBignum object, OutputStream buffer) throws IOException { - // JRUBY-4751: RubyBignum.to_s() returns generic object - // representation (fixed in 1.5, but we maintain backwards - // compatibility; call to_s(IRubyObject[]) then - ByteList bytes = ((RubyString) object.to_s(IRubyObject.NULL_ARRAY)).getByteList(); - buffer.write(bytes.unsafeBytes(), bytes.begin(), bytes.length()); + BigInteger bigInt = object.getValue(); + buffer.write(bigInt.toString().getBytes(StandardCharsets.UTF_8)); } }; @@ -261,8 +260,7 @@ void generate(Session session, RubyBignum object, OutputStream buffer) throws IO new Handler() { @Override void generate(Session session, RubyFixnum object, OutputStream buffer) throws IOException { - ByteList bytes = object.to_s().getByteList(); - buffer.write(bytes.unsafeBytes(), bytes.begin(), bytes.length()); + buffer.write(ConvertBytes.longToCharBytes(object.getLongValue())); } }; @@ -270,16 +268,14 @@ void generate(Session session, RubyFixnum object, OutputStream buffer) throws IO new Handler() { @Override void generate(Session session, RubyFloat object, OutputStream buffer) throws IOException { - if (object.isInfinite() || object.isNaN()) { + double value = object.getValue(); + + if (Double.isInfinite(value) || Double.isNaN(value)) { if (!session.getState().allowNaN()) { - throw Utils.newException(session.getContext(), - Utils.M_GENERATOR_ERROR, - object + " not allowed in JSON"); + throw Utils.newException(session.getContext(), Utils.M_GENERATOR_ERROR, object + " not allowed in JSON"); } } - double value = RubyFloat.num2dbl(object); - buffer.write(Double.toString(value).getBytes(StandardCharsets.UTF_8)); } }; From 5d498fba550c3782a111d149c03612775dc9c479 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 09:39:36 -0600 Subject: [PATCH 06/15] Plumb ThreadContext through parser and generator This eliminates the context field on ParserSession, ByteListTranscoder, and Generator.Session as well as accesses of the runtime through it. These runtime structures should generally be passed on the stack rather than being stored into long-lived objects, even if they may be GCed quickly. --- java/src/json/ext/ByteListTranscoder.java | 52 ++-- java/src/json/ext/Generator.java | 157 ++++++------ java/src/json/ext/Parser.java | 282 +++++++++++----------- java/src/json/ext/Parser.rl | 136 +++++------ java/src/json/ext/StringDecoder.java | 46 ++-- java/src/json/ext/StringEncoder.java | 12 +- 6 files changed, 322 insertions(+), 363 deletions(-) diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index 0fedcabd..dc294f05 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -17,8 +17,6 @@ * using UTF-8 ByteLists as both input and output. */ abstract class ByteListTranscoder { - protected final ThreadContext context; - protected ByteList src; protected int srcEnd; /** Position where the last read character started */ @@ -36,10 +34,6 @@ abstract class ByteListTranscoder { */ private int quoteStart = -1; - protected ByteListTranscoder(ThreadContext context) { - this.context = context; - } - protected void init(ByteList src, OutputStream out) { this.init(src, 0, src.length(), out); } @@ -70,52 +64,52 @@ private char next() { * Reads an UTF-8 character from the input and returns its code point, * while advancing the input position. * - *

Raises an {@link #invalidUtf8()} exception if an invalid byte + *

Raises an {@link #invalidUtf8(ThreadContext)} exception if an invalid byte * is found. */ - protected int readUtf8Char() { + protected int readUtf8Char(ThreadContext context) { charStart = pos; char head = next(); if (head <= 0x7f) { // 0b0xxxxxxx (ASCII) return head; } if (head <= 0xbf) { // 0b10xxxxxx - throw invalidUtf8(); // tail byte with no head + throw invalidUtf8(context); // tail byte with no head } if (head <= 0xdf) { // 0b110xxxxx - ensureMin(1); + ensureMin(context, 1); int cp = ((head & 0x1f) << 6) - | nextPart(); - if (cp < 0x0080) throw invalidUtf8(); + | nextPart(context); + if (cp < 0x0080) throw invalidUtf8(context); return cp; } if (head <= 0xef) { // 0b1110xxxx - ensureMin(2); + ensureMin(context, 2); int cp = ((head & 0x0f) << 12) - | (nextPart() << 6) - | nextPart(); - if (cp < 0x0800) throw invalidUtf8(); + | (nextPart(context) << 6) + | nextPart(context); + if (cp < 0x0800) throw invalidUtf8(context); return cp; } if (head <= 0xf7) { // 0b11110xxx - ensureMin(3); + ensureMin(context, 3); int cp = ((head & 0x07) << 18) - | (nextPart() << 12) - | (nextPart() << 6) - | nextPart(); - if (!Character.isValidCodePoint(cp)) throw invalidUtf8(); + | (nextPart(context) << 12) + | (nextPart(context) << 6) + | nextPart(context); + if (!Character.isValidCodePoint(cp)) throw invalidUtf8(context); return cp; } // 0b11111xxx? - throw invalidUtf8(); + throw invalidUtf8(context); } /** * Throws a GeneratorError if the input list doesn't have at least this * many bytes left. */ - protected void ensureMin(int n) { - if (pos + n > srcEnd) throw incompleteUtf8(); + protected void ensureMin(ThreadContext context, int n) { + if (pos + n > srcEnd) throw incompleteUtf8(context); } /** @@ -124,10 +118,10 @@ protected void ensureMin(int n) { * *

Throws a GeneratorError if the byte is not a valid tail. */ - private int nextPart() { + private int nextPart(ThreadContext context) { char c = next(); // tail bytes must be 0b10xxxxxx - if ((c & 0xc0) != 0x80) throw invalidUtf8(); + if ((c & 0xc0) != 0x80) throw invalidUtf8(context); return c & 0x3f; } @@ -161,9 +155,9 @@ protected void append(byte[] origin, int start, int length) throws IOException { } - protected abstract RaiseException invalidUtf8(); + protected abstract RaiseException invalidUtf8(ThreadContext context); - protected RaiseException incompleteUtf8() { - return invalidUtf8(); + protected RaiseException incompleteUtf8(ThreadContext context) { + return invalidUtf8(context); } } diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index d4de15c2..2b3cd581 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -43,13 +43,13 @@ private Generator() { * Encodes the given object as a JSON string, using the given handler. */ static RubyString generateJson(ThreadContext context, T object, Handler handler) { - Session session = new Session(context, null); - return session.infect(handler.generateNew(session, object)); + Session session = new Session(null); + return session.infect(handler.generateNew(context, session, object)); } static RubyString generateJson(ThreadContext context, T object, Handler handler, IRubyObject arg0) { - Session session = new Session(context, arg0); - return session.infect(handler.generateNew(session, object)); + Session session = new Session(arg0); + return session.infect(handler.generateNew(context, session, object)); } /** @@ -73,15 +73,15 @@ static RubyString generateJson(ThreadContext context, T public static IRubyObject generateJson(ThreadContext context, T object, GeneratorState config, IRubyObject io) { - Session session = new Session(context, config); + Session session = new Session(config); Handler handler = getHandlerFor(context.runtime, object); if (io.isNil()) { - return handler.generateNew(session, object); + return handler.generateNew(context, session, object); } BufferedOutputStream buffer = new BufferedOutputStream(new IOOutputStream(io), IO_BUFFER_SIZE); - handler.generateToBuffer(session, object, buffer); + handler.generateToBuffer(context, session, object, buffer); return io; } @@ -123,11 +123,10 @@ private static Handler getHandlerFor(Ruby run * object; any handler directly called by container handlers (arrays and * hashes/objects) shares this object with its caller. * - *

Note that anything called indirectly (via {@link GENERIC_HANDLER}) + *

Note that anything called indirectly (via {@link #GENERIC_HANDLER}) * won't be part of the session. */ static class Session { - private final ThreadContext context; private GeneratorState state; private IRubyObject possibleState; private RuntimeInfo info; @@ -136,40 +135,31 @@ static class Session { private boolean tainted = false; private boolean untrusted = false; - Session(ThreadContext context, GeneratorState state) { - this.context = context; + Session(GeneratorState state) { this.state = state; } - Session(ThreadContext context, IRubyObject possibleState) { - this.context = context; + Session(IRubyObject possibleState) { this.possibleState = possibleState == null || possibleState.isNil() ? null : possibleState; } - public ThreadContext getContext() { - return context; - } - - public Ruby getRuntime() { - return context.getRuntime(); - } - - public GeneratorState getState() { + public GeneratorState getState(ThreadContext context) { if (state == null) { - state = GeneratorState.fromState(context, getInfo(), possibleState); + state = GeneratorState.fromState(context, getInfo(context), possibleState); } return state; } - public RuntimeInfo getInfo() { - if (info == null) info = RuntimeInfo.forRuntime(getRuntime()); + public RuntimeInfo getInfo(ThreadContext context) { + if (info == null) info = RuntimeInfo.forRuntime(context.runtime); return info; } - public StringEncoder getStringEncoder() { + public StringEncoder getStringEncoder(ThreadContext context) { if (stringEncoder == null) { - stringEncoder = new StringEncoder(context, getState().asciiOnly(), getState().scriptSafe()); + GeneratorState state = getState(context); + stringEncoder = new StringEncoder(state.asciiOnly(), state.scriptSafe()); } return stringEncoder; } @@ -195,26 +185,26 @@ private static abstract class Handler { * given object will take. Used for allocating enough buffer space * before invoking other methods. */ - int guessSize(Session session, T object) { + int guessSize(ThreadContext context, Session session, T object) { return 4; } - RubyString generateNew(Session session, T object) { - ByteListDirectOutputStream buffer = new ByteListDirectOutputStream(guessSize(session, object)); - generateToBuffer(session, object, buffer); - return RubyString.newString(session.getRuntime(), buffer.toByteListDirect(UTF8Encoding.INSTANCE)); + RubyString generateNew(ThreadContext context, Session session, T object) { + ByteListDirectOutputStream buffer = new ByteListDirectOutputStream(guessSize(context, session, object)); + generateToBuffer(context, session, object, buffer); + return RubyString.newString(context.runtime, buffer.toByteListDirect(UTF8Encoding.INSTANCE)); } - void generateToBuffer(Session session, T object, OutputStream buffer) { + void generateToBuffer(ThreadContext context, Session session, T object, OutputStream buffer) { try { - generate(session, object, buffer); + generate(context, session, object, buffer); buffer.flush(); } catch (IOException ioe) { - throw session.getRuntime().newIOErrorFromException(ioe); + throw context.runtime.newIOErrorFromException(ioe); } } - abstract void generate(Session session, T object, OutputStream buffer) throws IOException; + abstract void generate(ThreadContext context, Session session, T object, OutputStream buffer) throws IOException; } /** @@ -229,17 +219,17 @@ private KeywordHandler(String keyword) { } @Override - int guessSize(Session session, T object) { + int guessSize(ThreadContext context, Session session, T object) { return keyword.length; } @Override - RubyString generateNew(Session session, T object) { - return RubyString.newStringShared(session.getRuntime(), keyword); + RubyString generateNew(ThreadContext context, Session session, T object) { + return RubyString.newStringShared(context.runtime, keyword); } @Override - void generate(Session session, T object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, T object, OutputStream buffer) throws IOException { buffer.write(keyword); } } @@ -250,7 +240,7 @@ void generate(Session session, T object, OutputStream buffer) throws IOException static final Handler BIGNUM_HANDLER = new Handler() { @Override - void generate(Session session, RubyBignum object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, RubyBignum object, OutputStream buffer) throws IOException { BigInteger bigInt = object.getValue(); buffer.write(bigInt.toString().getBytes(StandardCharsets.UTF_8)); } @@ -259,7 +249,7 @@ void generate(Session session, RubyBignum object, OutputStream buffer) throws IO static final Handler FIXNUM_HANDLER = new Handler() { @Override - void generate(Session session, RubyFixnum object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, RubyFixnum object, OutputStream buffer) throws IOException { buffer.write(ConvertBytes.longToCharBytes(object.getLongValue())); } }; @@ -267,12 +257,12 @@ void generate(Session session, RubyFixnum object, OutputStream buffer) throws IO static final Handler FLOAT_HANDLER = new Handler() { @Override - void generate(Session session, RubyFloat object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, RubyFloat object, OutputStream buffer) throws IOException { double value = object.getValue(); if (Double.isInfinite(value) || Double.isNaN(value)) { - if (!session.getState().allowNaN()) { - throw Utils.newException(session.getContext(), Utils.M_GENERATOR_ERROR, object + " not allowed in JSON"); + if (!session.getState(context).allowNaN()) { + throw Utils.newException(context, Utils.M_GENERATOR_ERROR, object + " not allowed in JSON"); } } @@ -284,8 +274,8 @@ void generate(Session session, RubyFloat object, OutputStream buffer) throws IOE static final Handler ARRAY_HANDLER = new Handler() { @Override - int guessSize(Session session, RubyArray object) { - GeneratorState state = session.getState(); + int guessSize(ThreadContext context, Session session, RubyArray object) { + GeneratorState state = session.getState(context); int depth = state.getDepth(); int perItem = 4 // prealloc @@ -295,10 +285,8 @@ int guessSize(Session session, RubyArray object) { } @Override - void generate(Session session, RubyArray object, OutputStream buffer) throws IOException { - ThreadContext context = session.getContext(); - Ruby runtime = context.getRuntime(); - GeneratorState state = session.getState(); + void generate(ThreadContext context, Session session, RubyArray object, OutputStream buffer) throws IOException { + GeneratorState state = session.getState(context); int depth = state.increaseDepth(); if (object.isEmpty()) { @@ -307,6 +295,8 @@ void generate(Session session, RubyArray object, OutputStream buffer) throws IOE return; } + Ruby runtime = context.runtime; + ByteList indentUnit = state.getIndent(); byte[] shift = Utils.repeat(indentUnit, depth); @@ -321,6 +311,7 @@ void generate(Session session, RubyArray object, OutputStream buffer) throws IOE buffer.write((byte)'['); buffer.write(arrayNl.bytes()); boolean firstItem = true; + for (int i = 0, t = object.getLength(); i < t; i++) { IRubyObject element = object.eltInternal(i); session.infectBy(element); @@ -331,7 +322,7 @@ void generate(Session session, RubyArray object, OutputStream buffer) throws IOE } buffer.write(shift); Handler handler = (Handler) getHandlerFor(runtime, element); - handler.generate(session, element, buffer); + handler.generate(context, session, element, buffer); } state.decreaseDepth(); @@ -348,8 +339,8 @@ void generate(Session session, RubyArray object, OutputStream buffer) throws IOE static final Handler HASH_HANDLER = new Handler() { @Override - int guessSize(Session session, RubyHash object) { - GeneratorState state = session.getState(); + int guessSize(ThreadContext context, Session session, RubyHash object) { + GeneratorState state = session.getState(context); int perItem = 12 // key, colon, comma + (state.getDepth() + 1) * state.getIndent().length() @@ -359,11 +350,8 @@ int guessSize(Session session, RubyHash object) { } @Override - void generate(final Session session, RubyHash object, - final OutputStream buffer) throws IOException { - ThreadContext context = session.getContext(); - final Ruby runtime = context.getRuntime(); - final GeneratorState state = session.getState(); + void generate(ThreadContext context, final Session session, RubyHash object, final OutputStream buffer) throws IOException { + final GeneratorState state = session.getState(context); final int depth = state.increaseDepth(); if (object.isEmpty()) { @@ -372,6 +360,8 @@ void generate(final Session session, RubyHash object, return; } + final Ruby runtime = context.runtime; + final ByteList objectNl = state.getObjectNl(); final byte[] indent = Utils.repeat(state.getIndent(), depth); final ByteList spaceBefore = state.getSpaceBefore(); @@ -395,11 +385,11 @@ public void visit(IRubyObject key, IRubyObject value) { IRubyObject keyStr = key.callMethod(context, "to_s"); if (keyStr.getMetaClass() == runtime.getString()) { - STRING_HANDLER.generate(session, (RubyString) keyStr, buffer); + STRING_HANDLER.generate(context, session, (RubyString) keyStr, buffer); } else { Utils.ensureString(keyStr); Handler keyHandler = (Handler) getHandlerFor(runtime, keyStr); - keyHandler.generate(session, keyStr, buffer); + keyHandler.generate(context, session, keyStr, buffer); } session.infectBy(key); @@ -408,7 +398,7 @@ public void visit(IRubyObject key, IRubyObject value) { buffer.write(space.bytes()); Handler valueHandler = (Handler) getHandlerFor(runtime, value); - valueHandler.generate(session, value, buffer); + valueHandler.generate(context, session, value, buffer); session.infectBy(value); } catch (Throwable t) { Helpers.throwException(t); @@ -427,7 +417,7 @@ public void visit(IRubyObject key, IRubyObject value) { static final Handler STRING_HANDLER = new Handler() { @Override - int guessSize(Session session, RubyString object) { + int guessSize(ThreadContext context, Session session, RubyString object) { // for most applications, most strings will be just a set of // printable ASCII characters without any escaping, so let's // just allocate enough space for that + the quotes @@ -435,21 +425,20 @@ int guessSize(Session session, RubyString object) { } @Override - void generate(Session session, RubyString object, OutputStream buffer) throws IOException { - RuntimeInfo info = session.getInfo(); + void generate(ThreadContext context, Session session, RubyString object, OutputStream buffer) throws IOException { + RuntimeInfo info = session.getInfo(context); RubyString src; try { - if (object.encoding(session.getContext()) != info.utf8.get()) { - src = (RubyString)object.encode(session.getContext(), - info.utf8.get()); + if (object.encoding(context) != info.utf8.get()) { + src = (RubyString)object.encode(context, info.utf8.get()); } else { src = object; } - session.getStringEncoder().encode(src.getByteList(), buffer); + session.getStringEncoder(context).encode(context, src.getByteList(), buffer); } catch (RaiseException re) { - throw Utils.newException(session.getContext(), Utils.M_GENERATOR_ERROR, + throw Utils.newException(context, Utils.M_GENERATOR_ERROR, re.getMessage()); } } @@ -469,15 +458,15 @@ void generate(Session session, RubyString object, OutputStream buffer) throws IO static final Handler OBJECT_HANDLER = new Handler() { @Override - RubyString generateNew(Session session, IRubyObject object) { + RubyString generateNew(ThreadContext context, Session session, IRubyObject object) { RubyString str = object.asString(); - return STRING_HANDLER.generateNew(session, str); + return STRING_HANDLER.generateNew(context, session, str); } @Override - void generate(Session session, IRubyObject object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, IRubyObject object, OutputStream buffer) throws IOException { RubyString str = object.asString(); - STRING_HANDLER.generate(session, str, buffer); + STRING_HANDLER.generate(context, session, str, buffer); } }; @@ -488,24 +477,22 @@ void generate(Session session, IRubyObject object, OutputStream buffer) throws I static final Handler GENERIC_HANDLER = new Handler() { @Override - RubyString generateNew(Session session, IRubyObject object) { - if (session.getState().strict()) { - throw Utils.newException(session.getContext(), - Utils.M_GENERATOR_ERROR, - object + " not allowed in JSON"); + RubyString generateNew(ThreadContext context, Session session, IRubyObject object) { + GeneratorState state = session.getState(context); + if (state.strict()) { + throw Utils.newException(context, Utils.M_GENERATOR_ERROR, object + " not allowed in JSON"); } else if (object.respondsTo("to_json")) { - IRubyObject result = object.callMethod(session.getContext(), "to_json", - new IRubyObject[] {session.getState()}); + IRubyObject result = object.callMethod(context, "to_json", state); if (result instanceof RubyString) return (RubyString)result; - throw session.getRuntime().newTypeError("to_json must return a String"); + throw context.runtime.newTypeError("to_json must return a String"); } else { - return OBJECT_HANDLER.generateNew(session, object); + return OBJECT_HANDLER.generateNew(context, session, object); } } @Override - void generate(Session session, IRubyObject object, OutputStream buffer) throws IOException { - RubyString result = generateNew(session, object); + void generate(ThreadContext context, Session session, IRubyObject object, OutputStream buffer) throws IOException { + RubyString result = generateNew(context, session, object); ByteList bytes = result.getByteList(); buffer.write(bytes.unsafeBytes(), bytes.begin(), bytes.length()); } diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 74037d37..b1bd5c8f 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -41,7 +41,7 @@ * This is performed for you when you include "json/ext". * *

This class does not perform the actual parsing, just acts as an interface - * to Ruby code. When the {@link #parse()} method is invoked, a + * to Ruby code. When the {@link #parse(ThreadContext)} method is invoked, a * Parser.ParserSession object is instantiated, which handles the process. * * @author mernen @@ -175,7 +175,7 @@ public static IRubyObject parse(ThreadContext context, IRubyObject clazz, IRubyO @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { - Ruby runtime = context.getRuntime(); + Ruby runtime = context.runtime; if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); } @@ -278,7 +278,7 @@ private RubyString reinterpretEncoding(ThreadContext context, */ @JRubyMethod public IRubyObject parse(ThreadContext context) { - return new ParserSession(this, context, info).parse(); + return new ParserSession(this, context, info).parse(context); } /** @@ -288,15 +288,15 @@ public IRubyObject parse(ThreadContext context) { * used to construct this Parser. */ @JRubyMethod(name = "source") - public IRubyObject source_get() { - return checkAndGetSource().dup(); + public IRubyObject source_get(ThreadContext context) { + return checkAndGetSource(context).dup(); } - public RubyString checkAndGetSource() { + public RubyString checkAndGetSource(ThreadContext context) { if (vSource != null) { return vSource; } else { - throw getRuntime().newTypeError("uninitialized instance"); + throw context.runtime.newTypeError("uninitialized instance"); } } @@ -335,7 +335,6 @@ private IRubyObject createCustomDecimal(final ThreadContext context, final ByteL @SuppressWarnings("fallthrough") private static class ParserSession { private final Parser parser; - private final ThreadContext context; private final RuntimeInfo info; private final ByteList byteList; private final ByteList view; @@ -350,32 +349,27 @@ private static class ParserSession { private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) { this.parser = parser; - this.context = context; this.info = info; - this.byteList = parser.checkAndGetSource().getByteList(); + this.byteList = parser.checkAndGetSource(context).getByteList(); this.data = byteList.unsafeBytes(); this.view = new ByteList(data, false); - this.decoder = new StringDecoder(context); + this.decoder = new StringDecoder(); this.dc = new DoubleConverter(); } - private RaiseException unexpectedToken(int absStart, int absEnd) { - RubyString msg = getRuntime().newString("unexpected token at '") + private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) { + RubyString msg = context.runtime.newString("unexpected token at '") .cat(data, absStart, Math.min(absEnd - absStart, 32)) .cat((byte)'\''); - return newException(Utils.M_PARSER_ERROR, msg); - } - - private Ruby getRuntime() { - return context.getRuntime(); + return newException(context, Utils.M_PARSER_ERROR, msg); } -// line 397 "Parser.rl" +// line 391 "Parser.rl" -// line 379 "Parser.java" +// line 373 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -489,22 +483,22 @@ private static byte[] init__JSON_value_from_state_actions_0() static final int JSON_value_en_main = 1; -// line 503 "Parser.rl" +// line 497 "Parser.rl" - void parseValue(ParserResult res, int p, int pe) { + void parseValue(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject result = null; -// line 501 "Parser.java" +// line 495 "Parser.java" { cs = JSON_value_start; } -// line 510 "Parser.rl" +// line 504 "Parser.rl" -// line 508 "Parser.java" +// line 502 "Parser.java" { int _klen; int _trans = 0; @@ -530,13 +524,13 @@ void parseValue(ParserResult res, int p, int pe) { while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 488 "Parser.rl" +// line 482 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 540 "Parser.java" +// line 534 "Parser.java" } } @@ -599,45 +593,45 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 405 "Parser.rl" +// line 399 "Parser.rl" { - result = getRuntime().getNil(); + result = context.nil; } break; case 1: -// line 408 "Parser.rl" +// line 402 "Parser.rl" { - result = getRuntime().getFalse(); + result = context.fals; } break; case 2: -// line 411 "Parser.rl" +// line 405 "Parser.rl" { - result = getRuntime().getTrue(); + result = context.tru; } break; case 3: -// line 414 "Parser.rl" +// line 408 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); } else { - throw unexpectedToken(p - 2, pe); + throw unexpectedToken(context, p - 2, pe); } } break; case 4: -// line 421 "Parser.rl" +// line 415 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); } else { - throw unexpectedToken(p - 7, pe); + throw unexpectedToken(context, p - 7, pe); } } break; case 5: -// line 428 "Parser.rl" +// line 422 "Parser.rl" { if (pe > p + 8 && absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) { @@ -648,15 +642,15 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } - parseFloat(res, p, pe); + parseFloat(context, res, p, pe); if (res.result != null) { result = res.result; {p = (( res.p))-1;} } - parseInteger(res, p, pe); + parseInteger(context, res, p, pe); if (res.result != null) { result = res.result; {p = (( res.p))-1;} @@ -666,9 +660,9 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 6: -// line 454 "Parser.rl" +// line 448 "Parser.rl" { - parseString(res, p, pe); + parseString(context, res, p, pe); if (res.result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} @@ -679,10 +673,10 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 7: -// line 464 "Parser.rl" +// line 458 "Parser.rl" { currentNesting++; - parseArray(res, p, pe); + parseArray(context, res, p, pe); currentNesting--; if (res.result == null) { p--; @@ -694,10 +688,10 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 8: -// line 476 "Parser.rl" +// line 470 "Parser.rl" { currentNesting++; - parseObject(res, p, pe); + parseObject(context, res, p, pe); currentNesting--; if (res.result == null) { p--; @@ -708,7 +702,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } } break; -// line 712 "Parser.java" +// line 706 "Parser.java" } } } @@ -728,7 +722,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) break; } } -// line 511 "Parser.rl" +// line 505 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { if (parser.freeze) { @@ -741,7 +735,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } -// line 745 "Parser.java" +// line 739 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -840,33 +834,33 @@ private static byte[] init__JSON_integer_trans_actions_0() static final int JSON_integer_en_main = 1; -// line 533 "Parser.rl" +// line 527 "Parser.rl" - void parseInteger(ParserResult res, int p, int pe) { - int new_p = parseIntegerInternal(p, pe); + void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { + int new_p = parseIntegerInternal(context, p, pe); if (new_p == -1) { res.update(null, p); return; } - RubyInteger number = createInteger(p, new_p); + RubyInteger number = createInteger(context, p, new_p); res.update(number, new_p + 1); return; } - int parseIntegerInternal(int p, int pe) { + int parseIntegerInternal(ThreadContext context, int p, int pe) { int cs = EVIL; -// line 862 "Parser.java" +// line 856 "Parser.java" { cs = JSON_integer_start; } -// line 550 "Parser.rl" +// line 544 "Parser.rl" int memo = p; -// line 870 "Parser.java" +// line 864 "Parser.java" { int _klen; int _trans = 0; @@ -947,13 +941,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 527 "Parser.rl" +// line 521 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 957 "Parser.java" +// line 951 "Parser.java" } } } @@ -973,7 +967,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) break; } } -// line 552 "Parser.rl" +// line 546 "Parser.rl" if (cs < JSON_integer_first_final) { return -1; @@ -982,8 +976,8 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) return p; } - RubyInteger createInteger(int p, int new_p) { - Ruby runtime = getRuntime(); + RubyInteger createInteger(ThreadContext context, int p, int new_p) { + Ruby runtime = context.runtime; ByteList num = absSubSequence(p, new_p); return bytesToInum(runtime, num); } @@ -993,7 +987,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) { } -// line 997 "Parser.java" +// line 991 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -1095,11 +1089,11 @@ private static byte[] init__JSON_float_trans_actions_0() static final int JSON_float_en_main = 1; -// line 585 "Parser.rl" +// line 579 "Parser.rl" - void parseFloat(ParserResult res, int p, int pe) { - int new_p = parseFloatInternal(p, pe); + void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { + int new_p = parseFloatInternal(context, p, pe); if (new_p == -1) { res.update(null, p); return; @@ -1110,19 +1104,19 @@ void parseFloat(ParserResult res, int p, int pe) { res.update(number, new_p + 1); } - int parseFloatInternal(int p, int pe) { + int parseFloatInternal(ThreadContext context, int p, int pe) { int cs = EVIL; -// line 1118 "Parser.java" +// line 1112 "Parser.java" { cs = JSON_float_start; } -// line 603 "Parser.rl" +// line 597 "Parser.rl" int memo = p; -// line 1126 "Parser.java" +// line 1120 "Parser.java" { int _klen; int _trans = 0; @@ -1203,13 +1197,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 576 "Parser.rl" +// line 570 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1213 "Parser.java" +// line 1207 "Parser.java" } } } @@ -1229,7 +1223,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) break; } } -// line 605 "Parser.rl" +// line 599 "Parser.rl" if (cs < JSON_float_first_final) { return -1; @@ -1239,7 +1233,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) } -// line 1243 "Parser.java" +// line 1237 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1341,23 +1335,23 @@ private static byte[] init__JSON_string_trans_actions_0() static final int JSON_string_en_main = 1; -// line 644 "Parser.rl" +// line 638 "Parser.rl" - void parseString(ParserResult res, int p, int pe) { + void parseString(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject result = null; -// line 1353 "Parser.java" +// line 1347 "Parser.java" { cs = JSON_string_start; } -// line 651 "Parser.rl" +// line 645 "Parser.rl" int memo = p; -// line 1361 "Parser.java" +// line 1355 "Parser.java" { int _klen; int _trans = 0; @@ -1438,12 +1432,12 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 619 "Parser.rl" +// line 613 "Parser.rl" { int offset = byteList.begin(); - ByteList decoded = decoder.decode(byteList, memo + 1 - offset, + ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset, p - offset); - result = getRuntime().newString(decoded); + result = context.runtime.newString(decoded); if (result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} @@ -1453,13 +1447,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) } break; case 1: -// line 632 "Parser.rl" +// line 626 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1463 "Parser.java" +// line 1457 "Parser.java" } } } @@ -1479,7 +1473,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) break; } } -// line 653 "Parser.rl" +// line 647 "Parser.rl" if (parser.createAdditions) { RubyHash matchString = parser.match_string; @@ -1501,7 +1495,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) { if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { if (parser.deprecatedCreateAdditions) { - klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); + context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); } result = klass.callMethod(context, "json_create", result); } @@ -1515,7 +1509,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) { string.force_encoding(context, info.utf8.get()); if (parser.freeze) { string.setFrozen(true); - string = getRuntime().freezeAndDedupString(string); + string = context.runtime.freezeAndDedupString(string); } res.update(string, p + 1); } else { @@ -1527,7 +1521,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) { } -// line 1531 "Parser.java" +// line 1525 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1694,34 +1688,34 @@ private static byte[] init__JSON_array_trans_actions_0() static final int JSON_array_en_main = 1; -// line 738 "Parser.rl" +// line 732 "Parser.rl" - void parseArray(ParserResult res, int p, int pe) { + void parseArray(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { - throw newException(Utils.M_NESTING_ERROR, + throw newException(context, Utils.M_NESTING_ERROR, "nesting of " + currentNesting + " is too deep"); } IRubyObject result; - if (parser.arrayClass == getRuntime().getArray()) { - result = RubyArray.newArray(getRuntime()); + if (parser.arrayClass == context.runtime.getArray()) { + result = RubyArray.newArray(context.runtime); } else { result = parser.arrayClass.newInstance(context, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK); } -// line 1718 "Parser.java" +// line 1712 "Parser.java" { cs = JSON_array_start; } -// line 757 "Parser.rl" +// line 751 "Parser.rl" -// line 1725 "Parser.java" +// line 1719 "Parser.java" { int _klen; int _trans = 0; @@ -1764,7 +1758,7 @@ else if ( _widec > _JSON_array_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 705 "Parser.rl" +// line 699 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -1834,14 +1828,14 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 707 "Parser.rl" +// line 701 "Parser.rl" { - parseValue(res, p, pe); + parseValue(context, res, p, pe); if (res.result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } else { - if (parser.arrayClass == getRuntime().getArray()) { + if (parser.arrayClass == context.runtime.getArray()) { ((RubyArray)result).append(res.result); } else { result.callMethod(context, "<<", res.result); @@ -1851,13 +1845,13 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) } break; case 1: -// line 722 "Parser.rl" +// line 716 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1861 "Parser.java" +// line 1855 "Parser.java" } } } @@ -1877,17 +1871,17 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) break; } } -// line 758 "Parser.rl" +// line 752 "Parser.rl" if (cs >= JSON_array_first_final) { res.update(result, p + 1); } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } -// line 1891 "Parser.java" +// line 1885 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -2064,24 +2058,24 @@ private static byte[] init__JSON_object_trans_actions_0() static final int JSON_object_en_main = 1; -// line 819 "Parser.rl" +// line 813 "Parser.rl" - void parseObject(ParserResult res, int p, int pe) { + void parseObject(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject lastName = null; boolean objectDefault = true; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { - throw newException(Utils.M_NESTING_ERROR, + throw newException(context, Utils.M_NESTING_ERROR, "nesting of " + currentNesting + " is too deep"); } // this is guaranteed to be a RubyHash due to the earlier // allocator test at OptionsReader#getClass IRubyObject result; - if (parser.objectClass == getRuntime().getHash()) { - result = RubyHash.newHash(getRuntime()); + if (parser.objectClass == context.runtime.getHash()) { + result = RubyHash.newHash(context.runtime); } else { objectDefault = false; result = parser.objectClass.newInstance(context, @@ -2089,14 +2083,14 @@ void parseObject(ParserResult res, int p, int pe) { } -// line 2093 "Parser.java" +// line 2087 "Parser.java" { cs = JSON_object_start; } -// line 843 "Parser.rl" +// line 837 "Parser.rl" -// line 2100 "Parser.java" +// line 2094 "Parser.java" { int _klen; int _trans = 0; @@ -2139,7 +2133,7 @@ else if ( _widec > _JSON_object_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 772 "Parser.rl" +// line 766 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -2209,14 +2203,14 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 774 "Parser.rl" +// line 768 "Parser.rl" { - parseValue(res, p, pe); + parseValue(context, res, p, pe); if (res.result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } else { - if (parser.objectClass == getRuntime().getHash()) { + if (parser.objectClass == context.runtime.getHash()) { ((RubyHash)result).op_aset(context, lastName, res.result); } else { result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result }); @@ -2226,9 +2220,9 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } break; case 1: -// line 789 "Parser.rl" +// line 783 "Parser.rl" { - parseString(res, p, pe); + parseString(context, res, p, pe); if (res.result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} @@ -2244,13 +2238,13 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } break; case 2: -// line 805 "Parser.rl" +// line 799 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 2254 "Parser.java" +// line 2248 "Parser.java" } } } @@ -2270,7 +2264,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) break; } } -// line 844 "Parser.rl" +// line 838 "Parser.rl" if (cs < JSON_object_first_final) { res.update(null, p + 1); @@ -2295,7 +2289,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { if (parser.deprecatedCreateAdditions) { - klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); + context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); } returnedResult = klass.callMethod(context, "json_create", result); @@ -2306,7 +2300,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } -// line 2310 "Parser.java" +// line 2304 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { @@ -2409,26 +2403,26 @@ private static byte[] init__JSON_trans_actions_0() static final int JSON_en_main = 1; -// line 898 "Parser.rl" +// line 892 "Parser.rl" - public IRubyObject parseImplemetation() { + public IRubyObject parseImplementation(ThreadContext context) { int cs = EVIL; int p, pe; IRubyObject result = null; ParserResult res = new ParserResult(); -// line 2423 "Parser.java" +// line 2417 "Parser.java" { cs = JSON_start; } -// line 907 "Parser.rl" +// line 901 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2432 "Parser.java" +// line 2426 "Parser.java" { int _klen; int _trans = 0; @@ -2509,9 +2503,9 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) switch ( _JSON_actions[_acts++] ) { case 0: -// line 884 "Parser.rl" +// line 878 "Parser.rl" { - parseValue(res, p, pe); + parseValue(context, res, p, pe); if (res.result == null) { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} @@ -2521,7 +2515,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) } } break; -// line 2525 "Parser.java" +// line 2519 "Parser.java" } } } @@ -2541,23 +2535,23 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) break; } } -// line 910 "Parser.rl" +// line 904 "Parser.rl" if (cs >= JSON_first_final && p == pe) { return result; } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } - public IRubyObject parse() { - return parseImplemetation(); + public IRubyObject parse(ThreadContext context) { + return parseImplementation(context); } /** * Updates the "view" bytelist with the new offsets and returns it. - * @param start - * @param end + * @param absStart + * @param absEnd */ private ByteList absSubSequence(int absStart, int absEnd) { view.setBegin(absStart); @@ -2573,18 +2567,16 @@ private IRubyObject getConstant(String name) { return parser.info.jsonModule.get().getConstant(name); } - private RaiseException newException(String className, String message) { + private RaiseException newException(ThreadContext context, String className, String message) { return Utils.newException(context, className, message); } - private RaiseException newException(String className, RubyString message) { + private RaiseException newException(ThreadContext context, String className, RubyString message) { return Utils.newException(context, className, message); } - private RaiseException newException(String className, - String messageBegin, ByteList messageEnd) { - return newException(className, - getRuntime().newString(messageBegin).cat(messageEnd)); + private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { + return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); } } } diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index 9d2b96d6..84d75a33 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -39,7 +39,7 @@ import static org.jruby.util.ConvertDouble.DoubleConverter; * This is performed for you when you include "json/ext". * *

This class does not perform the actual parsing, just acts as an interface - * to Ruby code. When the {@link #parse()} method is invoked, a + * to Ruby code. When the {@link #parse(ThreadContext)} method is invoked, a * Parser.ParserSession object is instantiated, which handles the process. * * @author mernen @@ -173,7 +173,7 @@ 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.runtime; if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); } @@ -276,7 +276,7 @@ public class Parser extends RubyObject { */ @JRubyMethod public IRubyObject parse(ThreadContext context) { - return new ParserSession(this, context, info).parse(); + return new ParserSession(this, context, info).parse(context); } /** @@ -286,15 +286,15 @@ public class Parser extends RubyObject { * used to construct this Parser. */ @JRubyMethod(name = "source") - public IRubyObject source_get() { - return checkAndGetSource().dup(); + public IRubyObject source_get(ThreadContext context) { + return checkAndGetSource(context).dup(); } - public RubyString checkAndGetSource() { + public RubyString checkAndGetSource(ThreadContext context) { if (vSource != null) { return vSource; } else { - throw getRuntime().newTypeError("uninitialized instance"); + throw context.runtime.newTypeError("uninitialized instance"); } } @@ -333,7 +333,6 @@ public class Parser extends RubyObject { @SuppressWarnings("fallthrough") private static class ParserSession { private final Parser parser; - private final ThreadContext context; private final RuntimeInfo info; private final ByteList byteList; private final ByteList view; @@ -348,24 +347,19 @@ public class Parser extends RubyObject { private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) { this.parser = parser; - this.context = context; this.info = info; - this.byteList = parser.checkAndGetSource().getByteList(); + this.byteList = parser.checkAndGetSource(context).getByteList(); this.data = byteList.unsafeBytes(); this.view = new ByteList(data, false); - this.decoder = new StringDecoder(context); + this.decoder = new StringDecoder(); this.dc = new DoubleConverter(); } - private RaiseException unexpectedToken(int absStart, int absEnd) { - RubyString msg = getRuntime().newString("unexpected token at '") + private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) { + RubyString msg = context.runtime.newString("unexpected token at '") .cat(data, absStart, Math.min(absEnd - absStart, 32)) .cat((byte)'\''); - return newException(Utils.M_PARSER_ERROR, msg); - } - - private Ruby getRuntime() { - return context.getRuntime(); + return newException(context, Utils.M_PARSER_ERROR, msg); } %%{ @@ -403,26 +397,26 @@ public class Parser extends RubyObject { write data; action parse_null { - result = getRuntime().getNil(); + result = context.nil; } action parse_false { - result = getRuntime().getFalse(); + result = context.fals; } action parse_true { - result = getRuntime().getTrue(); + result = context.tru; } action parse_nan { if (parser.allowNaN) { result = getConstant(CONST_NAN); } else { - throw unexpectedToken(p - 2, pe); + throw unexpectedToken(context, p - 2, pe); } } action parse_infinity { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); } else { - throw unexpectedToken(p - 7, pe); + throw unexpectedToken(context, p - 7, pe); } } action parse_number { @@ -435,15 +429,15 @@ public class Parser extends RubyObject { fhold; fbreak; } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } - parseFloat(res, fpc, pe); + parseFloat(context, res, fpc, pe); if (res.result != null) { result = res.result; fexec res.p; } - parseInteger(res, fpc, pe); + parseInteger(context, res, fpc, pe); if (res.result != null) { result = res.result; fexec res.p; @@ -452,7 +446,7 @@ public class Parser extends RubyObject { fbreak; } action parse_string { - parseString(res, fpc, pe); + parseString(context, res, fpc, pe); if (res.result == null) { fhold; fbreak; @@ -463,7 +457,7 @@ public class Parser extends RubyObject { } action parse_array { currentNesting++; - parseArray(res, fpc, pe); + parseArray(context, res, fpc, pe); currentNesting--; if (res.result == null) { fhold; @@ -475,7 +469,7 @@ public class Parser extends RubyObject { } action parse_object { currentNesting++; - parseObject(res, fpc, pe); + parseObject(context, res, fpc, pe); currentNesting--; if (res.result == null) { fhold; @@ -502,7 +496,7 @@ public class Parser extends RubyObject { ) %*exit; }%% - void parseValue(ParserResult res, int p, int pe) { + void parseValue(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject result = null; @@ -532,18 +526,18 @@ public class Parser extends RubyObject { main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9]? @exit ); }%% - void parseInteger(ParserResult res, int p, int pe) { - int new_p = parseIntegerInternal(p, pe); + void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { + int new_p = parseIntegerInternal(context, p, pe); if (new_p == -1) { res.update(null, p); return; } - RubyInteger number = createInteger(p, new_p); + RubyInteger number = createInteger(context, p, new_p); res.update(number, new_p + 1); return; } - int parseIntegerInternal(int p, int pe) { + int parseIntegerInternal(ThreadContext context, int p, int pe) { int cs = EVIL; %% write init; @@ -557,8 +551,8 @@ public class Parser extends RubyObject { return p; } - RubyInteger createInteger(int p, int new_p) { - Ruby runtime = getRuntime(); + RubyInteger createInteger(ThreadContext context, int p, int new_p) { + Ruby runtime = context.runtime; ByteList num = absSubSequence(p, new_p); return bytesToInum(runtime, num); } @@ -584,8 +578,8 @@ public class Parser extends RubyObject { ( ^[0-9Ee.\-]? @exit ); }%% - void parseFloat(ParserResult res, int p, int pe) { - int new_p = parseFloatInternal(p, pe); + void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { + int new_p = parseFloatInternal(context, p, pe); if (new_p == -1) { res.update(null, p); return; @@ -596,7 +590,7 @@ public class Parser extends RubyObject { res.update(number, new_p + 1); } - int parseFloatInternal(int p, int pe) { + int parseFloatInternal(ThreadContext context, int p, int pe) { int cs = EVIL; %% write init; @@ -618,9 +612,9 @@ public class Parser extends RubyObject { action parse_string { int offset = byteList.begin(); - ByteList decoded = decoder.decode(byteList, memo + 1 - offset, + ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset, p - offset); - result = getRuntime().newString(decoded); + result = context.runtime.newString(decoded); if (result == null) { fhold; fbreak; @@ -643,7 +637,7 @@ public class Parser extends RubyObject { ) '"' @exit; }%% - void parseString(ParserResult res, int p, int pe) { + void parseString(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject result = null; @@ -671,7 +665,7 @@ public class Parser extends RubyObject { if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { if (parser.deprecatedCreateAdditions) { - klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); + context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); } result = klass.callMethod(context, "json_create", result); } @@ -685,7 +679,7 @@ public class Parser extends RubyObject { string.force_encoding(context, info.utf8.get()); if (parser.freeze) { string.setFrozen(true); - string = getRuntime().freezeAndDedupString(string); + string = context.runtime.freezeAndDedupString(string); } res.update(string, p + 1); } else { @@ -705,12 +699,12 @@ public class Parser extends RubyObject { action allow_trailing_comma { parser.allowTrailingComma } action parse_value { - parseValue(res, fpc, pe); + parseValue(context, res, fpc, pe); if (res.result == null) { fhold; fbreak; } else { - if (parser.arrayClass == getRuntime().getArray()) { + if (parser.arrayClass == context.runtime.getArray()) { ((RubyArray)result).append(res.result); } else { result.callMethod(context, "<<", res.result); @@ -737,17 +731,17 @@ public class Parser extends RubyObject { end_array @exit; }%% - void parseArray(ParserResult res, int p, int pe) { + void parseArray(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { - throw newException(Utils.M_NESTING_ERROR, + throw newException(context, Utils.M_NESTING_ERROR, "nesting of " + currentNesting + " is too deep"); } IRubyObject result; - if (parser.arrayClass == getRuntime().getArray()) { - result = RubyArray.newArray(getRuntime()); + if (parser.arrayClass == context.runtime.getArray()) { + result = RubyArray.newArray(context.runtime); } else { result = parser.arrayClass.newInstance(context, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK); @@ -759,7 +753,7 @@ public class Parser extends RubyObject { if (cs >= JSON_array_first_final) { res.update(result, p + 1); } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } @@ -772,12 +766,12 @@ public class Parser extends RubyObject { action allow_trailing_comma { parser.allowTrailingComma } action parse_value { - parseValue(res, fpc, pe); + parseValue(context, res, fpc, pe); if (res.result == null) { fhold; fbreak; } else { - if (parser.objectClass == getRuntime().getHash()) { + if (parser.objectClass == context.runtime.getHash()) { ((RubyHash)result).op_aset(context, lastName, res.result); } else { result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result }); @@ -787,7 +781,7 @@ public class Parser extends RubyObject { } action parse_name { - parseString(res, fpc, pe); + parseString(context, res, fpc, pe); if (res.result == null) { fhold; fbreak; @@ -818,21 +812,21 @@ public class Parser extends RubyObject { ) @exit; }%% - void parseObject(ParserResult res, int p, int pe) { + void parseObject(ThreadContext context, ParserResult res, int p, int pe) { int cs = EVIL; IRubyObject lastName = null; boolean objectDefault = true; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { - throw newException(Utils.M_NESTING_ERROR, + throw newException(context, Utils.M_NESTING_ERROR, "nesting of " + currentNesting + " is too deep"); } // this is guaranteed to be a RubyHash due to the earlier // allocator test at OptionsReader#getClass IRubyObject result; - if (parser.objectClass == getRuntime().getHash()) { - result = RubyHash.newHash(getRuntime()); + if (parser.objectClass == context.runtime.getHash()) { + result = RubyHash.newHash(context.runtime); } else { objectDefault = false; result = parser.objectClass.newInstance(context, @@ -865,7 +859,7 @@ public class Parser extends RubyObject { if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { if (parser.deprecatedCreateAdditions) { - klass.getRuntime().getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); + context.runtime.getWarnings().warn("JSON.load implicit support for `create_additions: true` is deprecated and will be removed in 3.0, use JSON.unsafe_load or explicitly pass `create_additions: true`"); } returnedResult = klass.callMethod(context, "json_create", result); @@ -882,7 +876,7 @@ public class Parser extends RubyObject { write data; action parse_value { - parseValue(res, fpc, pe); + parseValue(context, res, fpc, pe); if (res.result == null) { fhold; fbreak; @@ -897,7 +891,7 @@ public class Parser extends RubyObject { ignore*; }%% - public IRubyObject parseImplemetation() { + public IRubyObject parseImplementation(ThreadContext context) { int cs = EVIL; int p, pe; IRubyObject result = null; @@ -911,18 +905,18 @@ public class Parser extends RubyObject { if (cs >= JSON_first_final && p == pe) { return result; } else { - throw unexpectedToken(p, pe); + throw unexpectedToken(context, p, pe); } } - public IRubyObject parse() { - return parseImplemetation(); + public IRubyObject parse(ThreadContext context) { + return parseImplementation(context); } /** * Updates the "view" bytelist with the new offsets and returns it. - * @param start - * @param end + * @param absStart + * @param absEnd */ private ByteList absSubSequence(int absStart, int absEnd) { view.setBegin(absStart); @@ -938,18 +932,16 @@ public class Parser extends RubyObject { return parser.info.jsonModule.get().getConstant(name); } - private RaiseException newException(String className, String message) { + private RaiseException newException(ThreadContext context, String className, String message) { return Utils.newException(context, className, message); } - private RaiseException newException(String className, RubyString message) { + private RaiseException newException(ThreadContext context, String className, RubyString message) { return Utils.newException(context, className, message); } - private RaiseException newException(String className, - String messageBegin, ByteList messageEnd) { - return newException(className, - getRuntime().newString(messageBegin).cat(messageEnd)); + private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { + return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); } } } diff --git a/java/src/json/ext/StringDecoder.java b/java/src/json/ext/StringDecoder.java index f4877e93..e2b49efb 100644 --- a/java/src/json/ext/StringDecoder.java +++ b/java/src/json/ext/StringDecoder.java @@ -26,16 +26,12 @@ final class StringDecoder extends ByteListTranscoder { // Array used for writing multi-byte characters into the buffer at once private final byte[] aux = new byte[4]; - StringDecoder(ThreadContext context) { - super(context); - } - - ByteList decode(ByteList src, int start, int end) { + ByteList decode(ThreadContext context, ByteList src, int start, int end) { try { ByteListDirectOutputStream out = new ByteListDirectOutputStream(end - start); init(src, start, end, out); while (hasNext()) { - handleChar(readUtf8Char()); + handleChar(context, readUtf8Char(context)); } quoteStop(pos); return out.toByteListDirect(src.getEncoding()); @@ -44,18 +40,18 @@ ByteList decode(ByteList src, int start, int end) { } } - private void handleChar(int c) throws IOException { + private void handleChar(ThreadContext context, int c) throws IOException { if (c == '\\') { quoteStop(charStart); - handleEscapeSequence(); + handleEscapeSequence(context); } else { quoteStart(); } } - private void handleEscapeSequence() throws IOException { - ensureMin(1); - switch (readUtf8Char()) { + private void handleEscapeSequence(ThreadContext context) throws IOException { + ensureMin(context, 1); + switch (readUtf8Char(context)) { case 'b': append('\b'); break; @@ -72,13 +68,13 @@ private void handleEscapeSequence() throws IOException { append('\t'); break; case 'u': - ensureMin(4); - int cp = readHex(); + ensureMin(context, 4); + int cp = readHex(context); if (Character.isHighSurrogate((char)cp)) { - handleLowSurrogate((char)cp); + handleLowSurrogate(context, (char)cp); } else if (Character.isLowSurrogate((char)cp)) { // low surrogate with no high surrogate - throw invalidUtf8(); + throw invalidUtf8(context); } else { writeUtf8Char(cp); } @@ -88,15 +84,15 @@ private void handleEscapeSequence() throws IOException { } } - private void handleLowSurrogate(char highSurrogate) throws IOException { + private void handleLowSurrogate(ThreadContext context, char highSurrogate) throws IOException { surrogatePairStart = charStart; - ensureMin(1); - int lowSurrogate = readUtf8Char(); + ensureMin(context, 1); + int lowSurrogate = readUtf8Char(context); if (lowSurrogate == '\\') { - ensureMin(5); - if (readUtf8Char() != 'u') throw invalidUtf8(); - lowSurrogate = readHex(); + ensureMin(context, 5); + if (readUtf8Char(context) != 'u') throw invalidUtf8(context); + lowSurrogate = readHex(context); } if (Character.isLowSurrogate((char)lowSurrogate)) { @@ -104,7 +100,7 @@ private void handleLowSurrogate(char highSurrogate) throws IOException { (char)lowSurrogate)); surrogatePairStart = -1; } else { - throw invalidUtf8(); + throw invalidUtf8(context); } } @@ -136,12 +132,12 @@ private byte tailByte(int value) { /** * Reads a 4-digit unsigned hexadecimal number from the source. */ - private int readHex() { + private int readHex(ThreadContext context) { int numberStart = pos; int result = 0; int length = 4; for (int i = 0; i < length; i++) { - int digit = readUtf8Char(); + int digit = readUtf8Char(context); int digitValue; if (digit >= '0' && digit <= '9') { digitValue = digit - '0'; @@ -159,7 +155,7 @@ private int readHex() { } @Override - protected RaiseException invalidUtf8() { + protected RaiseException invalidUtf8(ThreadContext context) { ByteList message = new ByteList( ByteList.plain("partial character in source, " + "but hit end near ")); diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index b1e7096e..b211c4ec 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -40,17 +40,16 @@ final class StringEncoder extends ByteListTranscoder { new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - StringEncoder(ThreadContext context, boolean asciiOnly, boolean scriptSafe) { - super(context); + StringEncoder(boolean asciiOnly, boolean scriptSafe) { this.asciiOnly = asciiOnly; this.scriptSafe = scriptSafe; } - void encode(ByteList src, OutputStream out) throws IOException { + void encode(ThreadContext context, ByteList src, OutputStream out) throws IOException { init(src, out); append('"'); while (hasNext()) { - handleChar(readUtf8Char()); + handleChar(readUtf8Char(context)); } quoteStop(pos); append('"'); @@ -120,8 +119,7 @@ private void escapeCodeUnit(char c, int auxOffset) { } @Override - protected RaiseException invalidUtf8() { - return Utils.newException(context, Utils.M_GENERATOR_ERROR, - "source sequence is illegal/malformed utf-8"); + protected RaiseException invalidUtf8(ThreadContext context) { + return Utils.newException(context, Utils.M_GENERATOR_ERROR, "source sequence is illegal/malformed utf-8"); } } From b9d21a84317c6114433c839d9fe45f68edc6cc42 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 10:06:44 -0600 Subject: [PATCH 07/15] Eliminate remaining argument list boxes --- java/src/json/ext/GeneratorState.java | 15 +- java/src/json/ext/Parser.java | 208 ++++++++++++++------------ java/src/json/ext/Parser.rl | 62 ++++---- java/src/json/ext/Utils.java | 4 +- 4 files changed, 158 insertions(+), 131 deletions(-) diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 0d8a3617..977444aa 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -156,8 +156,7 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info, // if the given parameter is a Hash, pass it to the instantiator if (context.getRuntime().getHash().isInstance(opts)) { - return (GeneratorState)klass.newInstance(context, - new IRubyObject[] {opts}, Block.NULL_BLOCK); + return (GeneratorState)klass.newInstance(context, opts, Block.NULL_BLOCK); } } @@ -194,9 +193,15 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info, *

set to true if U+2028, U+2029 and forward slashes should be escaped * in the json output to make it safe to include in a JavaScript tag (default: false) */ - @JRubyMethod(optional=1, visibility=Visibility.PRIVATE) - public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { - _configure(context, args.length > 0 ? args[0] : null); + @JRubyMethod(visibility=Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context) { + _configure(context, null); + return this; + } + + @JRubyMethod(visibility=Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { + _configure(context, arg0); return this; } diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index b1bd5c8f..f21897d4 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -20,13 +20,13 @@ import org.jruby.exceptions.JumpException; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.Block; +import org.jruby.runtime.Helpers; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; import org.jruby.util.ConvertBytes; -import org.jruby.util.ConvertDouble; import java.util.function.BiFunction; @@ -156,31 +156,44 @@ public Parser(Ruby runtime, RubyClass metaClass) { * */ - @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) - public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) { + @JRubyMethod(name = "new", meta = true) + public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, Block block) { Parser parser = (Parser)((RubyClass)clazz).allocate(); - parser.callInit(args, block); + parser.callInit(arg0, block); + + return parser; + } + + @JRubyMethod(name = "new", meta = true) + public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, IRubyObject arg1, Block block) { + Parser parser = (Parser)((RubyClass)clazz).allocate(); + + parser.callInit(arg0, arg1, block); return parser; } @JRubyMethod(meta=true) public static IRubyObject parse(ThreadContext context, IRubyObject clazz, IRubyObject source, IRubyObject opts) { - IRubyObject[] args = new IRubyObject[] {source, opts}; Parser parser = (Parser)((RubyClass)clazz).allocate(); - parser.callInit(args, null); + parser.callInit(source, opts, null); return parser.parse(context); } - @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE) - public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { + @JRubyMethod(visibility = Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { + return initialize(context, arg0, null); + } + + @JRubyMethod(visibility = Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) { Ruby runtime = context.runtime; if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); - } + } - OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null); + OptionsReader opts = new OptionsReader(context, arg1); this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.allowTrailingComma = opts.getBool("allow_trailing_comma", false); @@ -215,12 +228,9 @@ public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { } if(symbolizeNames && createAdditions) { - throw runtime.newArgumentError( - "options :symbolize_names and :create_additions cannot be " + - " used in conjunction" - ); + throw runtime.newArgumentError("options :symbolize_names and :create_additions cannot be used in conjunction"); } - this.vSource = args[0].convertToString(); + this.vSource = arg0.convertToString(); this.vSource = convertEncoding(context, vSource); return this; @@ -365,11 +375,11 @@ private RaiseException unexpectedToken(ThreadContext context, int absStart, int } -// line 391 "Parser.rl" +// line 401 "Parser.rl" -// line 373 "Parser.java" +// line 383 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -483,7 +493,7 @@ private static byte[] init__JSON_value_from_state_actions_0() static final int JSON_value_en_main = 1; -// line 497 "Parser.rl" +// line 507 "Parser.rl" void parseValue(ThreadContext context, ParserResult res, int p, int pe) { @@ -491,14 +501,14 @@ void parseValue(ThreadContext context, ParserResult res, int p, int pe) { IRubyObject result = null; -// line 495 "Parser.java" +// line 505 "Parser.java" { cs = JSON_value_start; } -// line 504 "Parser.rl" +// line 514 "Parser.rl" -// line 502 "Parser.java" +// line 512 "Parser.java" { int _klen; int _trans = 0; @@ -524,13 +534,13 @@ void parseValue(ThreadContext context, ParserResult res, int p, int pe) { while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 482 "Parser.rl" +// line 492 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 534 "Parser.java" +// line 544 "Parser.java" } } @@ -593,25 +603,25 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 399 "Parser.rl" +// line 409 "Parser.rl" { result = context.nil; } break; case 1: -// line 402 "Parser.rl" +// line 412 "Parser.rl" { result = context.fals; } break; case 2: -// line 405 "Parser.rl" +// line 415 "Parser.rl" { result = context.tru; } break; case 3: -// line 408 "Parser.rl" +// line 418 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); @@ -621,7 +631,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 4: -// line 415 "Parser.rl" +// line 425 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); @@ -631,7 +641,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 5: -// line 422 "Parser.rl" +// line 432 "Parser.rl" { if (pe > p + 8 && absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) { @@ -660,7 +670,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 6: -// line 448 "Parser.rl" +// line 458 "Parser.rl" { parseString(context, res, p, pe); if (res.result == null) { @@ -673,7 +683,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 7: -// line 458 "Parser.rl" +// line 468 "Parser.rl" { currentNesting++; parseArray(context, res, p, pe); @@ -688,7 +698,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 8: -// line 470 "Parser.rl" +// line 480 "Parser.rl" { currentNesting++; parseObject(context, res, p, pe); @@ -702,7 +712,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } } break; -// line 706 "Parser.java" +// line 716 "Parser.java" } } } @@ -722,7 +732,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) break; } } -// line 505 "Parser.rl" +// line 515 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { if (parser.freeze) { @@ -735,7 +745,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } -// line 739 "Parser.java" +// line 749 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -834,7 +844,7 @@ private static byte[] init__JSON_integer_trans_actions_0() static final int JSON_integer_en_main = 1; -// line 527 "Parser.rl" +// line 537 "Parser.rl" void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { @@ -852,15 +862,15 @@ int parseIntegerInternal(ThreadContext context, int p, int pe) { int cs = EVIL; -// line 856 "Parser.java" +// line 866 "Parser.java" { cs = JSON_integer_start; } -// line 544 "Parser.rl" +// line 554 "Parser.rl" int memo = p; -// line 864 "Parser.java" +// line 874 "Parser.java" { int _klen; int _trans = 0; @@ -941,13 +951,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 521 "Parser.rl" +// line 531 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 951 "Parser.java" +// line 961 "Parser.java" } } } @@ -967,7 +977,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) break; } } -// line 546 "Parser.rl" +// line 556 "Parser.rl" if (cs < JSON_integer_first_final) { return -1; @@ -987,7 +997,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) { } -// line 991 "Parser.java" +// line 1001 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -1089,7 +1099,7 @@ private static byte[] init__JSON_float_trans_actions_0() static final int JSON_float_en_main = 1; -// line 579 "Parser.rl" +// line 589 "Parser.rl" void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { @@ -1108,15 +1118,15 @@ int parseFloatInternal(ThreadContext context, int p, int pe) { int cs = EVIL; -// line 1112 "Parser.java" +// line 1122 "Parser.java" { cs = JSON_float_start; } -// line 597 "Parser.rl" +// line 607 "Parser.rl" int memo = p; -// line 1120 "Parser.java" +// line 1130 "Parser.java" { int _klen; int _trans = 0; @@ -1197,13 +1207,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 570 "Parser.rl" +// line 580 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1207 "Parser.java" +// line 1217 "Parser.java" } } } @@ -1223,7 +1233,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) break; } } -// line 599 "Parser.rl" +// line 609 "Parser.rl" if (cs < JSON_float_first_final) { return -1; @@ -1233,7 +1243,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) } -// line 1237 "Parser.java" +// line 1247 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1335,7 +1345,7 @@ private static byte[] init__JSON_string_trans_actions_0() static final int JSON_string_en_main = 1; -// line 638 "Parser.rl" +// line 648 "Parser.rl" void parseString(ThreadContext context, ParserResult res, int p, int pe) { @@ -1343,15 +1353,15 @@ void parseString(ThreadContext context, ParserResult res, int p, int pe) { IRubyObject result = null; -// line 1347 "Parser.java" +// line 1357 "Parser.java" { cs = JSON_string_start; } -// line 645 "Parser.rl" +// line 655 "Parser.rl" int memo = p; -// line 1355 "Parser.java" +// line 1365 "Parser.java" { int _klen; int _trans = 0; @@ -1432,7 +1442,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 613 "Parser.rl" +// line 623 "Parser.rl" { int offset = byteList.begin(); ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset, @@ -1447,13 +1457,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) } break; case 1: -// line 626 "Parser.rl" +// line 636 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1457 "Parser.java" +// line 1467 "Parser.java" } } } @@ -1473,22 +1483,14 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) break; } } -// line 647 "Parser.rl" +// line 657 "Parser.rl" if (parser.createAdditions) { RubyHash matchString = parser.match_string; if (matchString != null) { final IRubyObject[] memoArray = { result, null }; try { - matchString.visitAll(new RubyHash.Visitor() { - @Override - public void visit(IRubyObject pattern, IRubyObject klass) { - if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) { - memoArray[1] = klass; - throw JumpException.SPECIAL_JUMP; - } - } - }); + matchString.visitAll(context, MATCH_VISITOR, memoArray); } catch (JumpException e) { } if (memoArray[1] != null) { RubyClass klass = (RubyClass) memoArray[1]; @@ -1521,7 +1523,7 @@ public void visit(IRubyObject pattern, IRubyObject klass) { } -// line 1525 "Parser.java" +// line 1527 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1688,7 +1690,7 @@ private static byte[] init__JSON_array_trans_actions_0() static final int JSON_array_en_main = 1; -// line 732 "Parser.rl" +// line 734 "Parser.rl" void parseArray(ThreadContext context, ParserResult res, int p, int pe) { @@ -1708,14 +1710,14 @@ void parseArray(ThreadContext context, ParserResult res, int p, int pe) { } -// line 1712 "Parser.java" +// line 1714 "Parser.java" { cs = JSON_array_start; } -// line 751 "Parser.rl" +// line 753 "Parser.rl" -// line 1719 "Parser.java" +// line 1721 "Parser.java" { int _klen; int _trans = 0; @@ -1758,7 +1760,7 @@ else if ( _widec > _JSON_array_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 699 "Parser.rl" +// line 701 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -1828,7 +1830,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 701 "Parser.rl" +// line 703 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -1845,13 +1847,13 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) } break; case 1: -// line 716 "Parser.rl" +// line 718 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1855 "Parser.java" +// line 1857 "Parser.java" } } } @@ -1871,7 +1873,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) break; } } -// line 752 "Parser.rl" +// line 754 "Parser.rl" if (cs >= JSON_array_first_final) { res.update(result, p + 1); @@ -1881,7 +1883,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) } -// line 1885 "Parser.java" +// line 1887 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -2058,7 +2060,7 @@ private static byte[] init__JSON_object_trans_actions_0() static final int JSON_object_en_main = 1; -// line 813 "Parser.rl" +// line 815 "Parser.rl" void parseObject(ThreadContext context, ParserResult res, int p, int pe) { @@ -2083,14 +2085,14 @@ void parseObject(ThreadContext context, ParserResult res, int p, int pe) { } -// line 2087 "Parser.java" +// line 2089 "Parser.java" { cs = JSON_object_start; } -// line 837 "Parser.rl" +// line 839 "Parser.rl" -// line 2094 "Parser.java" +// line 2096 "Parser.java" { int _klen; int _trans = 0; @@ -2133,7 +2135,7 @@ else if ( _widec > _JSON_object_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 766 "Parser.rl" +// line 768 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -2203,7 +2205,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 768 "Parser.rl" +// line 770 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -2213,14 +2215,14 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) if (parser.objectClass == context.runtime.getHash()) { ((RubyHash)result).op_aset(context, lastName, res.result); } else { - result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result }); + Helpers.invoke(context, result, "[]=", lastName, res.result); } {p = (( res.p))-1;} } } break; case 1: -// line 783 "Parser.rl" +// line 785 "Parser.rl" { parseString(context, res, p, pe); if (res.result == null) { @@ -2238,13 +2240,13 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } break; case 2: -// line 799 "Parser.rl" +// line 801 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 2248 "Parser.java" +// line 2250 "Parser.java" } } } @@ -2264,7 +2266,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) break; } } -// line 838 "Parser.rl" +// line 840 "Parser.rl" if (cs < JSON_object_first_final) { res.update(null, p + 1); @@ -2300,7 +2302,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } -// line 2304 "Parser.java" +// line 2306 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { @@ -2403,7 +2405,7 @@ private static byte[] init__JSON_trans_actions_0() static final int JSON_en_main = 1; -// line 892 "Parser.rl" +// line 894 "Parser.rl" public IRubyObject parseImplementation(ThreadContext context) { @@ -2413,16 +2415,16 @@ public IRubyObject parseImplementation(ThreadContext context) { ParserResult res = new ParserResult(); -// line 2417 "Parser.java" +// line 2419 "Parser.java" { cs = JSON_start; } -// line 901 "Parser.rl" +// line 903 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2426 "Parser.java" +// line 2428 "Parser.java" { int _klen; int _trans = 0; @@ -2503,7 +2505,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) switch ( _JSON_actions[_acts++] ) { case 0: -// line 878 "Parser.rl" +// line 880 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -2515,7 +2517,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) } } break; -// line 2519 "Parser.java" +// line 2521 "Parser.java" } } } @@ -2535,7 +2537,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) break; } } -// line 904 "Parser.rl" +// line 906 "Parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -2578,5 +2580,15 @@ private RaiseException newException(ThreadContext context, String className, Rub private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); } + + RubyHash.VisitorWithState MATCH_VISITOR = new RubyHash.VisitorWithState() { + @Override + public void visit(ThreadContext context, RubyHash self, IRubyObject pattern, IRubyObject klass, int index, IRubyObject[] state) { + if (pattern.callMethod(context, "===", state[0]).isTrue()) { + state[1] = klass; + throw JumpException.SPECIAL_JUMP; + } + } + }; } } diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index 84d75a33..14451fab 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -18,13 +18,13 @@ import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.JumpException; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.Block; +import org.jruby.runtime.Helpers; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; import org.jruby.util.ConvertBytes; -import org.jruby.util.ConvertDouble; import java.util.function.BiFunction; @@ -154,31 +154,44 @@ public class Parser extends RubyObject { * */ - @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) - public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args, Block block) { + @JRubyMethod(name = "new", meta = true) + public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, Block block) { Parser parser = (Parser)((RubyClass)clazz).allocate(); - parser.callInit(args, block); + parser.callInit(arg0, block); + + return parser; + } + + @JRubyMethod(name = "new", meta = true) + public static IRubyObject newInstance(IRubyObject clazz, IRubyObject arg0, IRubyObject arg1, Block block) { + Parser parser = (Parser)((RubyClass)clazz).allocate(); + + parser.callInit(arg0, arg1, block); return parser; } @JRubyMethod(meta=true) public static IRubyObject parse(ThreadContext context, IRubyObject clazz, IRubyObject source, IRubyObject opts) { - IRubyObject[] args = new IRubyObject[] {source, opts}; Parser parser = (Parser)((RubyClass)clazz).allocate(); - parser.callInit(args, null); + parser.callInit(source, opts, null); return parser.parse(context); } - @JRubyMethod(required = 1, optional = 1, visibility = Visibility.PRIVATE) - public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { + @JRubyMethod(visibility = Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { + return initialize(context, arg0, null); + } + + @JRubyMethod(visibility = Visibility.PRIVATE) + public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) { Ruby runtime = context.runtime; if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); - } + } - OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null); + OptionsReader opts = new OptionsReader(context, arg1); this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.allowTrailingComma = opts.getBool("allow_trailing_comma", false); @@ -213,12 +226,9 @@ public class Parser extends RubyObject { } if(symbolizeNames && createAdditions) { - throw runtime.newArgumentError( - "options :symbolize_names and :create_additions cannot be " + - " used in conjunction" - ); + throw runtime.newArgumentError("options :symbolize_names and :create_additions cannot be used in conjunction"); } - this.vSource = args[0].convertToString(); + this.vSource = arg0.convertToString(); this.vSource = convertEncoding(context, vSource); return this; @@ -650,15 +660,7 @@ public class Parser extends RubyObject { if (matchString != null) { final IRubyObject[] memoArray = { result, null }; try { - matchString.visitAll(new RubyHash.Visitor() { - @Override - public void visit(IRubyObject pattern, IRubyObject klass) { - if (pattern.callMethod(context, "===", memoArray[0]).isTrue()) { - memoArray[1] = klass; - throw JumpException.SPECIAL_JUMP; - } - } - }); + matchString.visitAll(context, MATCH_VISITOR, memoArray); } catch (JumpException e) { } if (memoArray[1] != null) { RubyClass klass = (RubyClass) memoArray[1]; @@ -774,7 +776,7 @@ public class Parser extends RubyObject { if (parser.objectClass == context.runtime.getHash()) { ((RubyHash)result).op_aset(context, lastName, res.result); } else { - result.callMethod(context, "[]=", new IRubyObject[] { lastName, res.result }); + Helpers.invoke(context, result, "[]=", lastName, res.result); } fexec res.p; } @@ -943,5 +945,15 @@ public class Parser extends RubyObject { private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); } + + RubyHash.VisitorWithState MATCH_VISITOR = new RubyHash.VisitorWithState() { + @Override + public void visit(ThreadContext context, RubyHash self, IRubyObject pattern, IRubyObject klass, int index, IRubyObject[] state) { + if (pattern.callMethod(context, "===", state[0]).isTrue()) { + state[1] = klass; + throw JumpException.SPECIAL_JUMP; + } + } + }; } } diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java index ed6f8329..f8c70608 100644 --- a/java/src/json/ext/Utils.java +++ b/java/src/json/ext/Utils.java @@ -66,9 +66,7 @@ static RaiseException newException(ThreadContext context, String className, RubyString message) { RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); RubyClass klazz = info.jsonModule.get().getClass(className); - RubyException excptn = - (RubyException)klazz.newInstance(context, - new IRubyObject[] {message}, Block.NULL_BLOCK); + RubyException excptn = (RubyException)klazz.newInstance(context, message, Block.NULL_BLOCK); return new RaiseException(excptn); } From c29d9ae08f6491261e77e20637e77795a1e06048 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 11:08:42 -0600 Subject: [PATCH 08/15] Cleanups and suggestions from IntelliJ --- java/src/json/ext/Generator.java | 111 ++++++++++-------------- java/src/json/ext/GeneratorMethods.java | 26 +++--- java/src/json/ext/GeneratorState.java | 101 ++++++++------------- java/src/json/ext/OptionsReader.java | 6 +- java/src/json/ext/Parser.java | 69 +++------------ java/src/json/ext/Parser.rl | 83 +++++------------- java/src/json/ext/RuntimeInfo.java | 32 ++----- java/src/json/ext/StringDecoder.java | 4 +- java/src/json/ext/StringEncoder.java | 2 +- java/src/json/ext/Utils.java | 13 +-- 10 files changed, 145 insertions(+), 302 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 2b3cd581..18d46f18 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -5,7 +5,6 @@ */ package json.ext; -import org.jcodings.Encoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; @@ -15,7 +14,6 @@ import org.jruby.RubyFixnum; import org.jruby.RubyFloat; import org.jruby.RubyHash; -import org.jruby.RubyIO; import org.jruby.RubyString; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; @@ -29,7 +27,8 @@ import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; -import java.nio.charset.StandardCharsets; + +import static java.nio.charset.StandardCharsets.*; public final class Generator { @@ -44,12 +43,12 @@ private Generator() { */ static RubyString generateJson(ThreadContext context, T object, Handler handler) { Session session = new Session(null); - return session.infect(handler.generateNew(context, session, object)); + return handler.generateNew(context, session, object); } static RubyString generateJson(ThreadContext context, T object, Handler handler, IRubyObject arg0) { Session session = new Session(arg0); - return session.infect(handler.generateNew(context, session, object)); + return handler.generateNew(context, session, object); } /** @@ -93,21 +92,21 @@ static RubyString generateJson(ThreadContext context, T @SuppressWarnings("unchecked") private static Handler getHandlerFor(Ruby runtime, T object) { switch (((RubyBasicObject) object).getNativeClassIndex()) { - case NIL : return (Handler) NIL_HANDLER; - case TRUE : return (Handler) TRUE_HANDLER; - case FALSE : return (Handler) FALSE_HANDLER; - case FLOAT : return (Handler) FLOAT_HANDLER; - case FIXNUM : return (Handler) FIXNUM_HANDLER; - case BIGNUM : return (Handler) BIGNUM_HANDLER; + case NIL : return NIL_HANDLER; + case TRUE : return (Handler) TRUE_HANDLER; + case FALSE : return (Handler) FALSE_HANDLER; + case FLOAT : return (Handler) FLOAT_HANDLER; + case FIXNUM : return (Handler) FIXNUM_HANDLER; + case BIGNUM : return (Handler) BIGNUM_HANDLER; case STRING : - if (((RubyBasicObject) object).getMetaClass() != runtime.getString()) break; - return (Handler) STRING_HANDLER; + if (Helpers.metaclass(object) != runtime.getString()) break; + return (Handler) STRING_HANDLER; case ARRAY : - if (((RubyBasicObject) object).getMetaClass() != runtime.getArray()) break; - return (Handler) ARRAY_HANDLER; + if (Helpers.metaclass(object) != runtime.getArray()) break; + return (Handler) ARRAY_HANDLER; case HASH : - if (((RubyBasicObject) object).getMetaClass() != runtime.getHash()) break; - return (Handler) HASH_HANDLER; + if (Helpers.metaclass(object) != runtime.getHash()) break; + return (Handler) HASH_HANDLER; } return GENERIC_HANDLER; } @@ -132,9 +131,6 @@ static class Session { private RuntimeInfo info; private StringEncoder stringEncoder; - private boolean tainted = false; - private boolean untrusted = false; - Session(GeneratorState state) { this.state = state; } @@ -163,17 +159,6 @@ public StringEncoder getStringEncoder(ThreadContext context) { } return stringEncoder; } - - public void infectBy(IRubyObject object) { - if (object.isTaint()) tainted = true; - if (object.isUntrusted()) untrusted = true; - } - - public T infect(T object) { - if (tainted) object.setTaint(true); - if (untrusted) object.setUntrusted(true); - return object; - } } @@ -212,10 +197,10 @@ void generateToBuffer(ThreadContext context, Session session, T object, OutputSt */ private static class KeywordHandler extends Handler { - private byte[] keyword; + private final byte[] keyword; private KeywordHandler(String keyword) { - this.keyword = keyword.getBytes(StandardCharsets.UTF_8); + this.keyword = keyword.getBytes(UTF_8); } @Override @@ -242,7 +227,7 @@ void generate(ThreadContext context, Session session, T object, OutputStream buf @Override void generate(ThreadContext context, Session session, RubyBignum object, OutputStream buffer) throws IOException { BigInteger bigInt = object.getValue(); - buffer.write(bigInt.toString().getBytes(StandardCharsets.UTF_8)); + buffer.write(bigInt.toString().getBytes(UTF_8)); } }; @@ -266,15 +251,15 @@ void generate(ThreadContext context, Session session, RubyFloat object, OutputSt } } - buffer.write(Double.toString(value).getBytes(StandardCharsets.UTF_8)); + buffer.write(Double.toString(value).getBytes(UTF_8)); } }; private static final byte[] EMPTY_ARRAY_BYTES = "[]".getBytes(); - static final Handler ARRAY_HANDLER = - new Handler() { + static final Handler> ARRAY_HANDLER = + new Handler>() { @Override - int guessSize(ThreadContext context, Session session, RubyArray object) { + int guessSize(ThreadContext context, Session session, RubyArray object) { GeneratorState state = session.getState(context); int depth = state.getDepth(); int perItem = @@ -285,9 +270,9 @@ int guessSize(ThreadContext context, Session session, RubyArray object) { } @Override - void generate(ThreadContext context, Session session, RubyArray object, OutputStream buffer) throws IOException { + void generate(ThreadContext context, Session session, RubyArray object, OutputStream buffer) throws IOException { GeneratorState state = session.getState(context); - int depth = state.increaseDepth(); + int depth = state.increaseDepth(context); if (object.isEmpty()) { buffer.write(EMPTY_ARRAY_BYTES); @@ -306,27 +291,24 @@ void generate(ThreadContext context, Session session, RubyArray object, OutputSt System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1, arrayNl.length()); - session.infectBy(object); - buffer.write((byte)'['); buffer.write(arrayNl.bytes()); boolean firstItem = true; for (int i = 0, t = object.getLength(); i < t; i++) { IRubyObject element = object.eltInternal(i); - session.infectBy(element); if (firstItem) { firstItem = false; } else { buffer.write(delim); } buffer.write(shift); - Handler handler = (Handler) getHandlerFor(runtime, element); + Handler handler = getHandlerFor(runtime, element); handler.generate(context, session, element, buffer); } state.decreaseDepth(); - if (arrayNl.length() != 0) { + if (!arrayNl.isEmpty()) { buffer.write(arrayNl.bytes()); buffer.write(shift, 0, state.getDepth() * indentUnit.length()); } @@ -352,7 +334,7 @@ int guessSize(ThreadContext context, Session session, RubyHash object) { @Override void generate(ThreadContext context, final Session session, RubyHash object, final OutputStream buffer) throws IOException { final GeneratorState state = session.getState(context); - final int depth = state.increaseDepth(); + final int depth = state.increaseDepth(context); if (object.isEmpty()) { buffer.write(EMPTY_HASH_BYTES); @@ -360,54 +342,53 @@ void generate(ThreadContext context, final Session session, RubyHash object, fin return; } - final Ruby runtime = context.runtime; - final ByteList objectNl = state.getObjectNl(); + byte[] objectNLBytes = objectNl.unsafeBytes(); final byte[] indent = Utils.repeat(state.getIndent(), depth); final ByteList spaceBefore = state.getSpaceBefore(); final ByteList space = state.getSpace(); buffer.write((byte)'{'); - buffer.write(objectNl.bytes()); + buffer.write(objectNLBytes); final boolean[] firstPair = new boolean[]{true}; - object.visitAll(new RubyHash.Visitor() { + object.visitAll(context, new RubyHash.VisitorWithState() { @Override - public void visit(IRubyObject key, IRubyObject value) { + public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, boolean[] firstPair) { try { if (firstPair[0]) { firstPair[0] = false; } else { buffer.write((byte) ','); - buffer.write(objectNl.bytes()); + buffer.write(objectNLBytes); } - if (objectNl.length() != 0) buffer.write(indent); + if (!objectNl.isEmpty()) buffer.write(indent); + + Ruby runtime = context.runtime; IRubyObject keyStr = key.callMethod(context, "to_s"); if (keyStr.getMetaClass() == runtime.getString()) { STRING_HANDLER.generate(context, session, (RubyString) keyStr, buffer); } else { Utils.ensureString(keyStr); - Handler keyHandler = (Handler) getHandlerFor(runtime, keyStr); + Handler keyHandler = getHandlerFor(runtime, keyStr); keyHandler.generate(context, session, keyStr, buffer); } - session.infectBy(key); - buffer.write(spaceBefore.bytes()); + buffer.write(spaceBefore.unsafeBytes()); buffer.write((byte) ':'); - buffer.write(space.bytes()); + buffer.write(space.unsafeBytes()); - Handler valueHandler = (Handler) getHandlerFor(runtime, value); + Handler valueHandler = getHandlerFor(runtime, value); valueHandler.generate(context, session, value, buffer); - session.infectBy(value); } catch (Throwable t) { Helpers.throwException(t); } } - }); + }, firstPair); state.decreaseDepth(); - if (!firstPair[0] && objectNl.length() != 0) { - buffer.write(objectNl.bytes()); + if (!firstPair[0] && !objectNl.isEmpty()) { + buffer.write(objectNLBytes); } buffer.write(Utils.repeat(state.getIndent(), state.getDepth())); buffer.write((byte)'}'); @@ -445,11 +426,11 @@ void generate(ThreadContext context, Session session, RubyString object, OutputS }; static final Handler TRUE_HANDLER = - new KeywordHandler("true"); + new KeywordHandler<>("true"); static final Handler FALSE_HANDLER = - new KeywordHandler("false"); + new KeywordHandler<>("false"); static final Handler NIL_HANDLER = - new KeywordHandler("null"); + new KeywordHandler<>("null"); /** * The default handler (Object#to_json): coerces the object diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java index ec44f4b3..81f01162 100644 --- a/java/src/json/ext/GeneratorMethods.java +++ b/java/src/json/ext/GeneratorMethods.java @@ -12,7 +12,6 @@ import org.jruby.RubyFixnum; import org.jruby.RubyFloat; import org.jruby.RubyHash; -import org.jruby.RubyInteger; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyString; @@ -30,8 +29,8 @@ class GeneratorMethods { /** * Populates the given module with all modules and their methods - * @param info - * @param generatorMethodsModule The module to populate + * @param info The current RuntimeInfo + * @param module The module to populate * (normally JSON::Generator::GeneratorMethods) */ static void populate(RuntimeInfo info, RubyModule module) { @@ -45,19 +44,18 @@ static void populate(RuntimeInfo info, RubyModule module) { defineMethods(module, "String", RbString.class); defineMethods(module, "TrueClass", RbTrue.class); - info.stringExtendModule = new WeakReference(module.defineModuleUnder("String") - .defineModuleUnder("Extend")); + info.stringExtendModule = new WeakReference<>(module.defineModuleUnder("String").defineModuleUnder("Extend")); info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class); } /** * Convenience method for defining methods on a submodule. - * @param parentModule - * @param submoduleName - * @param klass + * @param parentModule the parent module + * @param submoduleName the submodule + * @param klass the class from which to define methods */ private static void defineMethods(RubyModule parentModule, - String submoduleName, Class klass) { + String submoduleName, Class klass) { RubyModule submodule = parentModule.defineModuleUnder(submoduleName); submodule.defineAnnotatedMethods(klass); } @@ -77,12 +75,12 @@ public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRub public static class RbArray { @JRubyMethod public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf) { - return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER); + return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER); } @JRubyMethod public static IRubyObject to_json(ThreadContext context, IRubyObject vSelf, IRubyObject arg0) { - return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER, arg0); + return Generator.generateJson(context, (RubyArray)vSelf, Generator.ARRAY_HANDLER, arg0); } } @@ -154,7 +152,7 @@ public static IRubyObject to_json_raw_object(ThreadContext context, IRubyObject private static RubyHash toJsonRawObject(ThreadContext context, RubyString self) { - Ruby runtime = context.getRuntime(); + Ruby runtime = context.runtime; RubyHash result = RubyHash.newHash(runtime); IRubyObject createId = RuntimeInfo.forRuntime(runtime) @@ -174,7 +172,7 @@ private static RubyHash toJsonRawObject(ThreadContext context, @JRubyMethod(module=true) public static IRubyObject included(ThreadContext context, IRubyObject vSelf, IRubyObject module) { - RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); + RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); return module.callMethod(context, "extend", info.stringExtendModule.get()); } } @@ -190,7 +188,7 @@ public static class StringExtend { @JRubyMethod public static IRubyObject json_create(ThreadContext context, IRubyObject vSelf, IRubyObject vHash) { - Ruby runtime = context.getRuntime(); + Ruby runtime = context.runtime; RubyHash o = vHash.convertToHash(); IRubyObject rawData = o.fastARef(runtime.newString("raw")); if (rawData == null) { diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 977444aa..34a3f9b8 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -108,11 +108,7 @@ public class GeneratorState extends RubyObject { */ private int depth = 0; - static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new GeneratorState(runtime, klazz); - } - }; + static final ObjectAllocator ALLOCATOR = GeneratorState::new; public GeneratorState(Ruby runtime, RubyClass metaClass) { super(runtime, metaClass); @@ -126,15 +122,13 @@ public GeneratorState(Ruby runtime, RubyClass metaClass) { * configured by opts, something else to create an * unconfigured instance. If opts is a State * object, it is just returned. - * @param clazzParam The receiver of the method call - * ({@link RubyClass} State) + * @param context The current thread context + * @param klass The receiver of the method call ({@link RubyClass} State) * @param opts The object to use as a base for the new State - * @param block The block passed to the method * @return A GeneratorState as determined above */ @JRubyMethod(meta=true) - public static IRubyObject from_state(ThreadContext context, - IRubyObject klass, IRubyObject opts) { + public static IRubyObject from_state(ThreadContext context, IRubyObject klass, IRubyObject opts) { return fromState(context, opts); } @@ -144,7 +138,7 @@ public static IRubyObject generate(ThreadContext context, IRubyObject klass, IRu } static GeneratorState fromState(ThreadContext context, IRubyObject opts) { - return fromState(context, RuntimeInfo.forRuntime(context.getRuntime()), opts); + return fromState(context, RuntimeInfo.forRuntime(context.runtime), opts); } static GeneratorState fromState(ThreadContext context, RuntimeInfo info, @@ -155,7 +149,7 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info, if (klass.isInstance(opts)) return (GeneratorState)opts; // if the given parameter is a Hash, pass it to the instantiator - if (context.getRuntime().getHash().isInstance(opts)) { + if (context.runtime.getHash().isInstance(opts)) { return (GeneratorState)klass.newInstance(context, opts, Block.NULL_BLOCK); } } @@ -166,9 +160,9 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info, /** * State#initialize(opts = {}) - * + *

* Instantiates a new State object, configured by opts. - * + *

* opts can have the following keys: * *

@@ -207,7 +201,7 @@ public IRubyObject initialize(ThreadContext context, IRubyObject arg0) { @JRubyMethod public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) { - Ruby runtime = context.getRuntime(); + Ruby runtime = context.runtime; if (!(vOrig instanceof GeneratorState)) { throw runtime.newTypeError(vOrig, getType()); } @@ -236,7 +230,7 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject vOrig) { @JRubyMethod(visibility = Visibility.PRIVATE) public IRubyObject _generate(ThreadContext context, IRubyObject obj, IRubyObject io) { IRubyObject result = Generator.generateJson(context, obj, this, io); - RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); + RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); if (!(result instanceof RubyString)) { return result; } @@ -244,7 +238,7 @@ public IRubyObject _generate(ThreadContext context, IRubyObject obj, IRubyObject RubyString resultString = result.convertToString(); if (resultString.getEncoding() != UTF8Encoding.INSTANCE) { if (resultString.isFrozen()) { - resultString = resultString.strDup(context.getRuntime()); + resultString = resultString.strDup(context.runtime); } resultString.force_encoding(context, info.utf8.get()); } @@ -252,17 +246,7 @@ public IRubyObject _generate(ThreadContext context, IRubyObject obj, IRubyObject return resultString; } - private static boolean matchClosingBrace(ByteList bl, int pos, int len, - int brace) { - for (int endPos = len - 1; endPos > pos; endPos--) { - int b = bl.get(endPos); - if (Character.isWhitespace(b)) continue; - return b == brace; - } - return false; - } - - @JRubyMethod(name="[]", required=1) + @JRubyMethod(name="[]") public IRubyObject op_aref(ThreadContext context, IRubyObject vName) { String name = vName.asJavaString(); if (getMetaClass().isMethodBound(name, true)) { @@ -273,16 +257,16 @@ public IRubyObject op_aref(ThreadContext context, IRubyObject vName) { } } - @JRubyMethod(name="[]=", required=2) + @JRubyMethod(name="[]=") public IRubyObject op_aset(ThreadContext context, IRubyObject vName, IRubyObject value) { String name = vName.asJavaString(); String nameWriter = name + "="; if (getMetaClass().isMethodBound(nameWriter, true)) { - return send(context, context.getRuntime().newString(nameWriter), value, Block.NULL_BLOCK); + return send(context, context.runtime.newString(nameWriter), value, Block.NULL_BLOCK); } else { getInstanceVariables().setInstanceVariable("@" + name, value); } - return context.getRuntime().getNil(); + return context.nil; } public ByteList getIndent() { @@ -291,7 +275,7 @@ public ByteList getIndent() { @JRubyMethod(name="indent") public RubyString indent_get(ThreadContext context) { - return context.getRuntime().newString(indent); + return context.runtime.newString(indent); } @JRubyMethod(name="indent=") @@ -306,7 +290,7 @@ public ByteList getSpace() { @JRubyMethod(name="space") public RubyString space_get(ThreadContext context) { - return context.getRuntime().newString(space); + return context.runtime.newString(space); } @JRubyMethod(name="space=") @@ -321,7 +305,7 @@ public ByteList getSpaceBefore() { @JRubyMethod(name="space_before") public RubyString space_before_get(ThreadContext context) { - return context.getRuntime().newString(spaceBefore); + return context.runtime.newString(spaceBefore); } @JRubyMethod(name="space_before=") @@ -337,7 +321,7 @@ public ByteList getObjectNl() { @JRubyMethod(name="object_nl") public RubyString object_nl_get(ThreadContext context) { - return context.getRuntime().newString(objectNl); + return context.runtime.newString(objectNl); } @JRubyMethod(name="object_nl=") @@ -353,7 +337,7 @@ public ByteList getArrayNl() { @JRubyMethod(name="array_nl") public RubyString array_nl_get(ThreadContext context) { - return context.getRuntime().newString(arrayNl); + return context.runtime.newString(arrayNl); } @JRubyMethod(name="array_nl=") @@ -365,19 +349,12 @@ public IRubyObject array_nl_set(ThreadContext context, @JRubyMethod(name="check_circular?") public RubyBoolean check_circular_p(ThreadContext context) { - return context.getRuntime().newBoolean(maxNesting != 0); - } - - /** - * Returns the maximum level of nesting configured for this state. - */ - public int getMaxNesting() { - return maxNesting; + return RubyBoolean.newBoolean(context, maxNesting != 0); } @JRubyMethod(name="max_nesting") public RubyInteger max_nesting_get(ThreadContext context) { - return context.getRuntime().newFixnum(maxNesting); + return context.runtime.newFixnum(maxNesting); } @JRubyMethod(name="max_nesting=") @@ -395,7 +372,7 @@ public boolean scriptSafe() { @JRubyMethod(name="script_safe", alias="escape_slash") public RubyBoolean script_safe_get(ThreadContext context) { - return context.getRuntime().newBoolean(scriptSafe); + return RubyBoolean.newBoolean(context, scriptSafe); } @JRubyMethod(name="script_safe=", alias="escape_slash=") @@ -406,7 +383,7 @@ public IRubyObject script_safe_set(IRubyObject script_safe) { @JRubyMethod(name="script_safe?", alias="escape_slash?") public RubyBoolean script_safe_p(ThreadContext context) { - return context.getRuntime().newBoolean(scriptSafe); + return RubyBoolean.newBoolean(context, scriptSafe); } /** @@ -418,7 +395,7 @@ public boolean strict() { @JRubyMethod(name={"strict","strict?"}) public RubyBoolean strict_get(ThreadContext context) { - return context.getRuntime().newBoolean(strict); + return RubyBoolean.newBoolean(context, strict); } @JRubyMethod(name="strict=") @@ -433,7 +410,7 @@ public boolean allowNaN() { @JRubyMethod(name="allow_nan?") public RubyBoolean allow_nan_p(ThreadContext context) { - return context.getRuntime().newBoolean(allowNaN); + return RubyBoolean.newBoolean(context, allowNaN); } public boolean asciiOnly() { @@ -442,12 +419,12 @@ public boolean asciiOnly() { @JRubyMethod(name="ascii_only?") public RubyBoolean ascii_only_p(ThreadContext context) { - return context.getRuntime().newBoolean(asciiOnly); + return RubyBoolean.newBoolean(context, asciiOnly); } @JRubyMethod(name="buffer_initial_length") public RubyInteger buffer_initial_length_get(ThreadContext context) { - return context.getRuntime().newFixnum(bufferInitialLength); + return context.runtime.newFixnum(bufferInitialLength); } @JRubyMethod(name="buffer_initial_length=") @@ -463,7 +440,7 @@ public int getDepth() { @JRubyMethod(name="depth") public RubyInteger depth_get(ThreadContext context) { - return context.getRuntime().newFixnum(depth); + return context.runtime.newFixnum(depth); } @JRubyMethod(name="depth=") @@ -474,7 +451,7 @@ public IRubyObject depth_set(IRubyObject vDepth) { private ByteList prepareByteList(ThreadContext context, IRubyObject value) { RubyString str = value.convertToString(); - RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); + RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); if (str.encoding(context) != info.utf8.get()) { str = (RubyString)str.encode(context, info.utf8.get()); } @@ -532,7 +509,7 @@ public IRubyObject _configure(ThreadContext context, IRubyObject vOpts) { */ @JRubyMethod(alias = "to_hash") public RubyHash to_h(ThreadContext context) { - Ruby runtime = context.getRuntime(); + Ruby runtime = context.runtime; RubyHash result = RubyHash.newHash(runtime); result.op_aset(context, runtime.newSymbol("indent"), indent_get(context)); @@ -553,26 +530,24 @@ public RubyHash to_h(ThreadContext context) { return result; } - public int increaseDepth() { + public int increaseDepth(ThreadContext context) { depth++; - checkMaxNesting(); + checkMaxNesting(context); return depth; } - public int decreaseDepth() { - return --depth; + public void decreaseDepth() { + --depth; } /** * Checks if the current depth is allowed as per this state's options. - * @param context - * @param depth The current depth + * @param context The current context */ - private void checkMaxNesting() { + private void checkMaxNesting(ThreadContext context) { if (maxNesting != 0 && depth > maxNesting) { depth--; - throw Utils.newException(getRuntime().getCurrentContext(), - Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep"); + throw Utils.newException(context, Utils.M_NESTING_ERROR, "nesting of " + depth + " is too deep"); } } } diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java index 70426d42..be19e609 100644 --- a/java/src/json/ext/OptionsReader.java +++ b/java/src/json/ext/OptionsReader.java @@ -22,7 +22,7 @@ final class OptionsReader { OptionsReader(ThreadContext context, IRubyObject vOpts) { this.context = context; - this.runtime = context.getRuntime(); + this.runtime = context.runtime; if (vOpts == null || vOpts.isNil()) { opts = null; } else if (vOpts.respondsTo("to_hash")) { @@ -41,7 +41,7 @@ private RuntimeInfo getRuntimeInfo() { } /** - * Efficiently looks up items with a {@link RubySymbol Symbol} key + * Efficiently looks up items with a {@link org.jruby.RubySymbol Symbol} key * @param key The Symbol name to look up for * @return The item in the {@link RubyHash Hash}, or null * if not found @@ -69,7 +69,7 @@ int getInt(String key, int defaultValue) { * @param key The Symbol name to look up for * @return null if the key is not in the Hash or if * its value evaluates to false - * @throws RaiseException TypeError if the value does not + * @throws org.jruby.exceptions.RaiseException TypeError if the value does not * evaluate to false and can't be * converted to string */ diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index f21897d4..4988db0c 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -71,11 +71,7 @@ public class Parser extends RubyObject { private static final String CONST_INFINITY = "Infinity"; private static final String CONST_MINUS_INFINITY = "MinusInfinity"; - static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new Parser(runtime, klazz); - } - }; + static final ObjectAllocator ALLOCATOR = Parser::new; /** * Multiple-value return for internal parser methods. @@ -119,7 +115,7 @@ public Parser(Ruby runtime, RubyClass metaClass) { *
The maximum depth of nesting allowed in the parsed data * structures. Disable depth checking with :max_nesting => false|nil|0, * it defaults to 100. - * + *

*

:allow_nan *
If set to true, allow NaN, * Infinity and -Infinity in defiance of RFC 4627 @@ -252,34 +248,6 @@ private RubyString convertEncoding(ThreadContext context, RubyString source) { return source; } - /** - * Checks the first four bytes of the given ByteList to infer its encoding, - * using the principle demonstrated on section 3 of RFC 4627 (JSON). - */ - private static String sniffByteList(ByteList bl) { - if (bl.length() < 4) return null; - if (bl.get(0) == 0 && bl.get(2) == 0) { - return bl.get(1) == 0 ? "utf-32be" : "utf-16be"; - } - if (bl.get(1) == 0 && bl.get(3) == 0) { - return bl.get(2) == 0 ? "utf-32le" : "utf-16le"; - } - return null; - } - - /** - * Assumes the given (binary) RubyString to be in the given encoding, then - * converts it to UTF-8. - */ - private RubyString reinterpretEncoding(ThreadContext context, - RubyString str, String sniffedEncoding) { - RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding); - RubyEncoding targetEncoding = info.utf8.get(); - RubyString dup = (RubyString)str.dup(); - dup.force_encoding(context, actualEncoding); - return (RubyString)dup.encode_bang(context, targetEncoding); - } - /** * Parser#parse() * @@ -351,11 +319,6 @@ private static class ParserSession { private final byte[] data; private final StringDecoder decoder; private int currentNesting = 0; - private final DoubleConverter dc; - - // initialization value for all state variables. - // no idea about the origins of this value, ask Flori ;) - private static final int EVIL = 0x666; private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) { this.parser = parser; @@ -364,7 +327,6 @@ private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) { this.data = byteList.unsafeBytes(); this.view = new ByteList(data, false); this.decoder = new StringDecoder(); - this.dc = new DoubleConverter(); } private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) { @@ -497,7 +459,7 @@ private static byte[] init__JSON_value_from_state_actions_0() void parseValue(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject result = null; @@ -848,18 +810,17 @@ private static byte[] init__JSON_integer_trans_actions_0() void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { - int new_p = parseIntegerInternal(context, p, pe); + int new_p = parseIntegerInternal(p, pe); if (new_p == -1) { res.update(null, p); return; } RubyInteger number = createInteger(context, p, new_p); res.update(number, new_p + 1); - return; } - int parseIntegerInternal(ThreadContext context, int p, int pe) { - int cs = EVIL; + int parseIntegerInternal(int p, int pe) { + int cs; // line 866 "Parser.java" @@ -1103,7 +1064,7 @@ private static byte[] init__JSON_float_trans_actions_0() void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { - int new_p = parseFloatInternal(context, p, pe); + int new_p = parseFloatInternal(p, pe); if (new_p == -1) { res.update(null, p); return; @@ -1114,8 +1075,8 @@ void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { res.update(number, new_p + 1); } - int parseFloatInternal(ThreadContext context, int p, int pe) { - int cs = EVIL; + int parseFloatInternal(int p, int pe) { + int cs; // line 1122 "Parser.java" @@ -1349,7 +1310,7 @@ private static byte[] init__JSON_string_trans_actions_0() void parseString(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject result = null; @@ -1694,7 +1655,7 @@ private static byte[] init__JSON_array_trans_actions_0() void parseArray(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { throw newException(context, Utils.M_NESTING_ERROR, @@ -2064,7 +2025,7 @@ private static byte[] init__JSON_object_trans_actions_0() void parseObject(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject lastName = null; boolean objectDefault = true; @@ -2409,7 +2370,7 @@ private static byte[] init__JSON_trans_actions_0() public IRubyObject parseImplementation(ThreadContext context) { - int cs = EVIL; + int cs; int p, pe; IRubyObject result = null; ParserResult res = new ParserResult(); @@ -2577,10 +2538,6 @@ private RaiseException newException(ThreadContext context, String className, Rub return Utils.newException(context, className, message); } - private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { - return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); - } - RubyHash.VisitorWithState MATCH_VISITOR = new RubyHash.VisitorWithState() { @Override public void visit(ThreadContext context, RubyHash self, IRubyObject pattern, IRubyObject klass, int index, IRubyObject[] state) { diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index 14451fab..f40b94e2 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -69,11 +69,7 @@ public class Parser extends RubyObject { private static final String CONST_INFINITY = "Infinity"; private static final String CONST_MINUS_INFINITY = "MinusInfinity"; - static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new Parser(runtime, klazz); - } - }; + static final ObjectAllocator ALLOCATOR = Parser::new; /** * Multiple-value return for internal parser methods. @@ -111,42 +107,42 @@ public class Parser extends RubyObject { * source. * It will be configured by the opts Hash. * opts can have the following keys: - * + *

*

*
:max_nesting *
The maximum depth of nesting allowed in the parsed data * structures. Disable depth checking with :max_nesting => false|nil|0, * it defaults to 100. - * + *

*

:allow_nan *
If set to true, allow NaN, * Infinity and -Infinity in defiance of RFC 4627 * to be parsed by the Parser. This option defaults to false. - * + *

*

:allow_trailing_comma *
If set to true, allow arrays and objects with a trailing * comma in defiance of RFC 4627 to be parsed by the Parser. * This option defaults to false. - * + *

*

:symbolize_names *
If set to true, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. - * + *

*

:create_additions *
If set to false, the Parser doesn't create additions * even if a matching class and create_id was found. This option * defaults to true. - * + *

*

:object_class *
Defaults to Hash. If another type is provided, it will be used * instead of Hash to represent JSON objects. The type must respond to * new without arguments, and return an object that respond to []=. - * + *

*

:array_class *
Defaults to Array. If another type is provided, it will be used * instead of Hash to represent JSON arrays. The type must respond to * new without arguments, and return an object that respond to <<. - * + *

*

:decimal_class *
Specifies which class to use instead of the default (Float) when * parsing decimal numbers. This class must accept a single string argument @@ -250,34 +246,6 @@ public class Parser extends RubyObject { return source; } - /** - * Checks the first four bytes of the given ByteList to infer its encoding, - * using the principle demonstrated on section 3 of RFC 4627 (JSON). - */ - private static String sniffByteList(ByteList bl) { - if (bl.length() < 4) return null; - if (bl.get(0) == 0 && bl.get(2) == 0) { - return bl.get(1) == 0 ? "utf-32be" : "utf-16be"; - } - if (bl.get(1) == 0 && bl.get(3) == 0) { - return bl.get(2) == 0 ? "utf-32le" : "utf-16le"; - } - return null; - } - - /** - * Assumes the given (binary) RubyString to be in the given encoding, then - * converts it to UTF-8. - */ - private RubyString reinterpretEncoding(ThreadContext context, - RubyString str, String sniffedEncoding) { - RubyEncoding actualEncoding = info.getEncoding(context, sniffedEncoding); - RubyEncoding targetEncoding = info.utf8.get(); - RubyString dup = (RubyString)str.dup(); - dup.force_encoding(context, actualEncoding); - return (RubyString)dup.encode_bang(context, targetEncoding); - } - /** * Parser#parse() * @@ -349,11 +317,6 @@ public class Parser extends RubyObject { private final byte[] data; private final StringDecoder decoder; private int currentNesting = 0; - private final DoubleConverter dc; - - // initialization value for all state variables. - // no idea about the origins of this value, ask Flori ;) - private static final int EVIL = 0x666; private ParserSession(Parser parser, ThreadContext context, RuntimeInfo info) { this.parser = parser; @@ -362,7 +325,6 @@ public class Parser extends RubyObject { this.data = byteList.unsafeBytes(); this.view = new ByteList(data, false); this.decoder = new StringDecoder(); - this.dc = new DoubleConverter(); } private RaiseException unexpectedToken(ThreadContext context, int absStart, int absEnd) { @@ -507,7 +469,7 @@ public class Parser extends RubyObject { }%% void parseValue(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject result = null; %% write init; @@ -537,18 +499,17 @@ public class Parser extends RubyObject { }%% void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { - int new_p = parseIntegerInternal(context, p, pe); + int new_p = parseIntegerInternal(p, pe); if (new_p == -1) { res.update(null, p); return; } RubyInteger number = createInteger(context, p, new_p); res.update(number, new_p + 1); - return; } - int parseIntegerInternal(ThreadContext context, int p, int pe) { - int cs = EVIL; + int parseIntegerInternal(int p, int pe) { + int cs; %% write init; int memo = p; @@ -589,7 +550,7 @@ public class Parser extends RubyObject { }%% void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { - int new_p = parseFloatInternal(context, p, pe); + int new_p = parseFloatInternal(p, pe); if (new_p == -1) { res.update(null, p); return; @@ -600,8 +561,8 @@ public class Parser extends RubyObject { res.update(number, new_p + 1); } - int parseFloatInternal(ThreadContext context, int p, int pe) { - int cs = EVIL; + int parseFloatInternal(int p, int pe) { + int cs; %% write init; int memo = p; @@ -648,7 +609,7 @@ public class Parser extends RubyObject { }%% void parseString(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject result = null; %% write init; @@ -734,7 +695,7 @@ public class Parser extends RubyObject { }%% void parseArray(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; if (parser.maxNesting > 0 && currentNesting > parser.maxNesting) { throw newException(context, Utils.M_NESTING_ERROR, @@ -815,7 +776,7 @@ public class Parser extends RubyObject { }%% void parseObject(ThreadContext context, ParserResult res, int p, int pe) { - int cs = EVIL; + int cs; IRubyObject lastName = null; boolean objectDefault = true; @@ -894,7 +855,7 @@ public class Parser extends RubyObject { }%% public IRubyObject parseImplementation(ThreadContext context) { - int cs = EVIL; + int cs; int p, pe; IRubyObject result = null; ParserResult res = new ParserResult(); @@ -942,10 +903,6 @@ public class Parser extends RubyObject { return Utils.newException(context, className, message); } - private RaiseException newException(ThreadContext context, String className, String messageBegin, ByteList messageEnd) { - return newException(context, className, context.runtime.newString(messageBegin).cat(messageEnd)); - } - RubyHash.VisitorWithState MATCH_VISITOR = new RubyHash.VisitorWithState() { @Override public void visit(ThreadContext context, RubyHash self, IRubyObject pattern, IRubyObject klass, int index, IRubyObject[] state) { diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java index 2323bd94..fcfaee3e 100644 --- a/java/src/json/ext/RuntimeInfo.java +++ b/java/src/json/ext/RuntimeInfo.java @@ -6,7 +6,6 @@ package json.ext; import java.lang.ref.WeakReference; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import org.jruby.Ruby; @@ -20,7 +19,7 @@ final class RuntimeInfo { // since the vast majority of cases runs just one runtime, // we optimize for that - private static WeakReference runtime1 = new WeakReference(null); + private static WeakReference runtime1 = new WeakReference<>(null); private static RuntimeInfo info1; // store remaining runtimes here (does not include runtime1) private static Map runtimes; @@ -39,22 +38,18 @@ final class RuntimeInfo { final WeakReference utf8; final WeakReference ascii8bit; - // other encodings - private final Map> encodings; private RuntimeInfo(Ruby runtime) { RubyClass encodingClass = runtime.getEncoding(); if (encodingClass == null) { // 1.8 mode utf8 = ascii8bit = null; - encodings = null; } else { ThreadContext context = runtime.getCurrentContext(); - utf8 = new WeakReference((RubyEncoding)RubyEncoding.find(context, + utf8 = new WeakReference<>((RubyEncoding) RubyEncoding.find(context, encodingClass, runtime.newString("utf-8"))); - ascii8bit = new WeakReference((RubyEncoding)RubyEncoding.find(context, + ascii8bit = new WeakReference<>((RubyEncoding) RubyEncoding.find(context, encodingClass, runtime.newString("ascii-8bit"))); - encodings = new HashMap>(); } } @@ -63,12 +58,12 @@ static RuntimeInfo initRuntime(Ruby runtime) { if (runtime1.get() == runtime) { return info1; } else if (runtime1.get() == null) { - runtime1 = new WeakReference(runtime); + runtime1 = new WeakReference<>(runtime); info1 = new RuntimeInfo(runtime); return info1; } else { if (runtimes == null) { - runtimes = new WeakHashMap(1); + runtimes = new WeakHashMap<>(1); } RuntimeInfo cache = runtimes.get(runtime); if (cache == null) { @@ -90,26 +85,13 @@ public static RuntimeInfo forRuntime(Ruby runtime) { } } - public RubyEncoding getEncoding(ThreadContext context, String name) { - synchronized (encodings) { - WeakReference encoding = encodings.get(name); - if (encoding == null) { - Ruby runtime = context.getRuntime(); - encoding = new WeakReference((RubyEncoding)RubyEncoding.find(context, - runtime.getEncoding(), runtime.newString(name))); - encodings.put(name, encoding); - } - return encoding.get(); - } - } - public GeneratorState getSafeStatePrototype(ThreadContext context) { if (safeStatePrototype == null) { IRubyObject value = jsonModule.get().getConstant("SAFE_STATE_PROTOTYPE"); if (!(value instanceof GeneratorState)) { - throw context.getRuntime().newTypeError(value, generatorStateClass.get()); + throw context.runtime.newTypeError(value, generatorStateClass.get()); } - safeStatePrototype = new WeakReference((GeneratorState)value); + safeStatePrototype = new WeakReference<>((GeneratorState) value); } return safeStatePrototype.get(); } diff --git a/java/src/json/ext/StringDecoder.java b/java/src/json/ext/StringDecoder.java index e2b49efb..ff984935 100644 --- a/java/src/json/ext/StringDecoder.java +++ b/java/src/json/ext/StringDecoder.java @@ -23,7 +23,7 @@ final class StringDecoder extends ByteListTranscoder { */ private int surrogatePairStart = -1; - // Array used for writing multi-byte characters into the buffer at once + // Array used for writing multibyte characters into the buffer at once private final byte[] aux = new byte[4]; ByteList decode(ThreadContext context, ByteList src, int start, int end) { @@ -162,6 +162,6 @@ protected RaiseException invalidUtf8(ThreadContext context) { int start = surrogatePairStart != -1 ? surrogatePairStart : charStart; message.append(src, start, srcEnd - start); return Utils.newException(context, Utils.M_PARSER_ERROR, - context.getRuntime().newString(message)); + context.runtime.newString(message)); } } diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index b211c4ec..8d23dfb9 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -23,7 +23,7 @@ final class StringEncoder extends ByteListTranscoder { // Escaped characters will reuse this array, to avoid new allocations // or appending them byte-by-byte private final byte[] aux = - new byte[] {/* First unicode character */ + new byte[] {/* First Unicode character */ '\\', 'u', 0, 0, 0, 0, /* Second unicode character (for surrogate pairs) */ '\\', 'u', 0, 0, 0, 0, diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java index f8c70608..a33652d7 100644 --- a/java/src/json/ext/Utils.java +++ b/java/src/json/ext/Utils.java @@ -9,7 +9,6 @@ import org.jruby.RubyArray; import org.jruby.RubyClass; import org.jruby.RubyException; -import org.jruby.RubyHash; import org.jruby.RubyString; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.Block; @@ -44,12 +43,6 @@ static RubyArray ensureArray(IRubyObject object) throws RaiseException { throw runtime.newTypeError(object, runtime.getArray()); } - static RubyHash ensureHash(IRubyObject object) throws RaiseException { - if (object instanceof RubyHash) return (RubyHash)object; - Ruby runtime = object.getRuntime(); - throw runtime.newTypeError(object, runtime.getHash()); - } - static RubyString ensureString(IRubyObject object) throws RaiseException { if (object instanceof RubyString) return (RubyString)object; Ruby runtime = object.getRuntime(); @@ -59,15 +52,15 @@ static RubyString ensureString(IRubyObject object) throws RaiseException { static RaiseException newException(ThreadContext context, String className, String message) { return newException(context, className, - context.getRuntime().newString(message)); + context.runtime.newString(message)); } static RaiseException newException(ThreadContext context, String className, RubyString message) { - RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime()); + RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); RubyClass klazz = info.jsonModule.get().getClass(className); RubyException excptn = (RubyException)klazz.newInstance(context, message, Block.NULL_BLOCK); - return new RaiseException(excptn); + return excptn.toThrowable(); } static byte[] repeat(ByteList a, int n) { From 5c41cd6df347bf9196592ccd3ba182918e1cb48a Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 16:58:58 -0600 Subject: [PATCH 09/15] Javadoc cleanup --- java/src/json/ext/Parser.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 4988db0c..4923d664 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -109,7 +109,7 @@ public Parser(Ruby runtime, RubyClass metaClass) { * source. * It will be configured by the opts Hash. * opts can have the following keys: - * + *

*

*
:max_nesting *
The maximum depth of nesting allowed in the parsed data @@ -120,31 +120,31 @@ public Parser(Ruby runtime, RubyClass metaClass) { *
If set to true, allow NaN, * Infinity and -Infinity in defiance of RFC 4627 * to be parsed by the Parser. This option defaults to false. - * + *

*

:allow_trailing_comma *
If set to true, allow arrays and objects with a trailing * comma in defiance of RFC 4627 to be parsed by the Parser. * This option defaults to false. - * + *

*

:symbolize_names *
If set to true, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. - * + *

*

:create_additions *
If set to false, the Parser doesn't create additions * even if a matching class and create_id was found. This option * defaults to true. - * + *

*

:object_class *
Defaults to Hash. If another type is provided, it will be used * instead of Hash to represent JSON objects. The type must respond to * new without arguments, and return an object that respond to []=. - * + *

*

:array_class *
Defaults to Array. If another type is provided, it will be used * instead of Hash to represent JSON arrays. The type must respond to * new without arguments, and return an object that respond to <<. - * + *

*

:decimal_class *
Specifies which class to use instead of the default (Float) when * parsing decimal numbers. This class must accept a single string argument From cbcc74b0631e5561f8db5d695929440aea590c6a Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 16:59:09 -0600 Subject: [PATCH 10/15] Eliminate weak refs to RubyEncoding objects It's more efficient to compare the Encoding object and for the rare cases where we need to transcode to UTF-8 just look it up again. --- java/src/json/ext/Generator.java | 8 +- java/src/json/ext/GeneratorState.java | 8 +- java/src/json/ext/OptionsReader.java | 6 +- java/src/json/ext/Parser.java | 164 +++++++++++++------------- java/src/json/ext/Parser.rl | 18 +-- java/src/json/ext/RuntimeInfo.java | 21 +--- 6 files changed, 108 insertions(+), 117 deletions(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 18d46f18..6db539a3 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -407,20 +407,18 @@ int guessSize(ThreadContext context, Session session, RubyString object) { @Override void generate(ThreadContext context, Session session, RubyString object, OutputStream buffer) throws IOException { - RuntimeInfo info = session.getInfo(context); RubyString src; try { - if (object.encoding(context) != info.utf8.get()) { - src = (RubyString)object.encode(context, info.utf8.get()); + if (object.getEncoding() != UTF8Encoding.INSTANCE) { + src = (RubyString)object.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } else { src = object; } session.getStringEncoder(context).encode(context, src.getByteList(), buffer); } catch (RaiseException re) { - throw Utils.newException(context, Utils.M_GENERATOR_ERROR, - re.getMessage()); + throw Utils.newException(context, Utils.M_GENERATOR_ERROR, re.getMessage()); } } }; diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 34a3f9b8..fdd433c6 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -240,7 +240,8 @@ public IRubyObject _generate(ThreadContext context, IRubyObject obj, IRubyObject if (resultString.isFrozen()) { resultString = resultString.strDup(context.runtime); } - resultString.force_encoding(context, info.utf8.get()); + resultString.setEncoding(UTF8Encoding.INSTANCE); + resultString.clearCodeRange(); } return resultString; @@ -451,9 +452,8 @@ public IRubyObject depth_set(IRubyObject vDepth) { private ByteList prepareByteList(ThreadContext context, IRubyObject value) { RubyString str = value.convertToString(); - RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); - if (str.encoding(context) != info.utf8.get()) { - str = (RubyString)str.encode(context, info.utf8.get()); + if (str.getEncoding() != UTF8Encoding.INSTANCE) { + str = (RubyString)str.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } return str.getByteList().dup(); } diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java index be19e609..ff976c38 100644 --- a/java/src/json/ext/OptionsReader.java +++ b/java/src/json/ext/OptionsReader.java @@ -5,6 +5,7 @@ */ package json.ext; +import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyHash; @@ -83,9 +84,8 @@ RubyString getString(String key, RubyString defaultValue) { if (value == null || !value.isTrue()) return defaultValue; RubyString str = value.convertToString(); - RuntimeInfo info = getRuntimeInfo(); - if (str.encoding(context) != info.utf8.get()) { - str = (RubyString)str.encode(context, info.utf8.get()); + if (str.getEncoding() != UTF8Encoding.INSTANCE) { + str = (RubyString)str.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } return str; } diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index 4923d664..47e66795 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -7,10 +7,12 @@ */ package json.ext; +import org.jcodings.Encoding; +import org.jcodings.specific.ASCIIEncoding; +import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyClass; -import org.jruby.RubyEncoding; import org.jruby.RubyFloat; import org.jruby.RubyHash; import org.jruby.RubyInteger; @@ -238,12 +240,13 @@ public IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObje * Returns the source string if no conversion is needed. */ private RubyString convertEncoding(ThreadContext context, RubyString source) { - RubyEncoding encoding = (RubyEncoding)source.encoding(context); - if (encoding == info.ascii8bit.get()) { + Encoding encoding = source.getEncoding(); + if (encoding == ASCIIEncoding.INSTANCE) { source = (RubyString) source.dup(); - source.force_encoding(context, info.utf8.get()); - } else { - source = (RubyString) source.encode(context, info.utf8.get()); + source.setEncoding(UTF8Encoding.INSTANCE); + source.clearCodeRange(); + } else if (encoding != UTF8Encoding.INSTANCE) { + source = (RubyString) source.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } return source; } @@ -337,11 +340,11 @@ private RaiseException unexpectedToken(ThreadContext context, int absStart, int } -// line 401 "Parser.rl" +// line 366 "Parser.rl" -// line 383 "Parser.java" +// line 348 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -455,7 +458,7 @@ private static byte[] init__JSON_value_from_state_actions_0() static final int JSON_value_en_main = 1; -// line 507 "Parser.rl" +// line 472 "Parser.rl" void parseValue(ThreadContext context, ParserResult res, int p, int pe) { @@ -463,14 +466,14 @@ void parseValue(ThreadContext context, ParserResult res, int p, int pe) { IRubyObject result = null; -// line 505 "Parser.java" +// line 470 "Parser.java" { cs = JSON_value_start; } -// line 514 "Parser.rl" +// line 479 "Parser.rl" -// line 512 "Parser.java" +// line 477 "Parser.java" { int _klen; int _trans = 0; @@ -496,13 +499,13 @@ void parseValue(ThreadContext context, ParserResult res, int p, int pe) { while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 492 "Parser.rl" +// line 457 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 544 "Parser.java" +// line 509 "Parser.java" } } @@ -565,25 +568,25 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 409 "Parser.rl" +// line 374 "Parser.rl" { result = context.nil; } break; case 1: -// line 412 "Parser.rl" +// line 377 "Parser.rl" { result = context.fals; } break; case 2: -// line 415 "Parser.rl" +// line 380 "Parser.rl" { result = context.tru; } break; case 3: -// line 418 "Parser.rl" +// line 383 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); @@ -593,7 +596,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 4: -// line 425 "Parser.rl" +// line 390 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); @@ -603,7 +606,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 5: -// line 432 "Parser.rl" +// line 397 "Parser.rl" { if (pe > p + 8 && absSubSequence(p, p + 9).equals(JSON_MINUS_INFINITY)) { @@ -632,7 +635,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 6: -// line 458 "Parser.rl" +// line 423 "Parser.rl" { parseString(context, res, p, pe); if (res.result == null) { @@ -645,7 +648,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 7: -// line 468 "Parser.rl" +// line 433 "Parser.rl" { currentNesting++; parseArray(context, res, p, pe); @@ -660,7 +663,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } break; case 8: -// line 480 "Parser.rl" +// line 445 "Parser.rl" { currentNesting++; parseObject(context, res, p, pe); @@ -674,7 +677,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } } break; -// line 716 "Parser.java" +// line 681 "Parser.java" } } } @@ -694,7 +697,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) break; } } -// line 515 "Parser.rl" +// line 480 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { if (parser.freeze) { @@ -707,7 +710,7 @@ else if ( data[p] > _JSON_value_trans_keys[_mid+1] ) } -// line 749 "Parser.java" +// line 714 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -806,7 +809,7 @@ private static byte[] init__JSON_integer_trans_actions_0() static final int JSON_integer_en_main = 1; -// line 537 "Parser.rl" +// line 502 "Parser.rl" void parseInteger(ThreadContext context, ParserResult res, int p, int pe) { @@ -823,15 +826,15 @@ int parseIntegerInternal(int p, int pe) { int cs; -// line 866 "Parser.java" +// line 830 "Parser.java" { cs = JSON_integer_start; } -// line 554 "Parser.rl" +// line 518 "Parser.rl" int memo = p; -// line 874 "Parser.java" +// line 838 "Parser.java" { int _klen; int _trans = 0; @@ -912,13 +915,13 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 531 "Parser.rl" +// line 496 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 961 "Parser.java" +// line 925 "Parser.java" } } } @@ -938,7 +941,7 @@ else if ( data[p] > _JSON_integer_trans_keys[_mid+1] ) break; } } -// line 556 "Parser.rl" +// line 520 "Parser.rl" if (cs < JSON_integer_first_final) { return -1; @@ -958,7 +961,7 @@ RubyInteger bytesToInum(Ruby runtime, ByteList num) { } -// line 1001 "Parser.java" +// line 965 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -1060,7 +1063,7 @@ private static byte[] init__JSON_float_trans_actions_0() static final int JSON_float_en_main = 1; -// line 589 "Parser.rl" +// line 553 "Parser.rl" void parseFloat(ThreadContext context, ParserResult res, int p, int pe) { @@ -1079,15 +1082,15 @@ int parseFloatInternal(int p, int pe) { int cs; -// line 1122 "Parser.java" +// line 1086 "Parser.java" { cs = JSON_float_start; } -// line 607 "Parser.rl" +// line 571 "Parser.rl" int memo = p; -// line 1130 "Parser.java" +// line 1094 "Parser.java" { int _klen; int _trans = 0; @@ -1168,13 +1171,13 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 580 "Parser.rl" +// line 544 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1217 "Parser.java" +// line 1181 "Parser.java" } } } @@ -1194,7 +1197,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) break; } } -// line 609 "Parser.rl" +// line 573 "Parser.rl" if (cs < JSON_float_first_final) { return -1; @@ -1204,7 +1207,7 @@ else if ( data[p] > _JSON_float_trans_keys[_mid+1] ) } -// line 1247 "Parser.java" +// line 1211 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1306,7 +1309,7 @@ private static byte[] init__JSON_string_trans_actions_0() static final int JSON_string_en_main = 1; -// line 648 "Parser.rl" +// line 612 "Parser.rl" void parseString(ThreadContext context, ParserResult res, int p, int pe) { @@ -1314,15 +1317,15 @@ void parseString(ThreadContext context, ParserResult res, int p, int pe) { IRubyObject result = null; -// line 1357 "Parser.java" +// line 1321 "Parser.java" { cs = JSON_string_start; } -// line 655 "Parser.rl" +// line 619 "Parser.rl" int memo = p; -// line 1365 "Parser.java" +// line 1329 "Parser.java" { int _klen; int _trans = 0; @@ -1403,7 +1406,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 623 "Parser.rl" +// line 587 "Parser.rl" { int offset = byteList.begin(); ByteList decoded = decoder.decode(context, byteList, memo + 1 - offset, @@ -1418,13 +1421,13 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) } break; case 1: -// line 636 "Parser.rl" +// line 600 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1467 "Parser.java" +// line 1431 "Parser.java" } } } @@ -1444,7 +1447,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) break; } } -// line 657 "Parser.rl" +// line 621 "Parser.rl" if (parser.createAdditions) { RubyHash matchString = parser.match_string; @@ -1469,7 +1472,8 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) if (cs >= JSON_string_first_final && result != null) { if (result instanceof RubyString) { RubyString string = (RubyString)result; - string.force_encoding(context, info.utf8.get()); + string.setEncoding(UTF8Encoding.INSTANCE); + string.clearCodeRange(); if (parser.freeze) { string.setFrozen(true); string = context.runtime.freezeAndDedupString(string); @@ -1484,7 +1488,7 @@ else if ( data[p] > _JSON_string_trans_keys[_mid+1] ) } -// line 1527 "Parser.java" +// line 1492 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1651,7 +1655,7 @@ private static byte[] init__JSON_array_trans_actions_0() static final int JSON_array_en_main = 1; -// line 734 "Parser.rl" +// line 699 "Parser.rl" void parseArray(ThreadContext context, ParserResult res, int p, int pe) { @@ -1671,14 +1675,14 @@ void parseArray(ThreadContext context, ParserResult res, int p, int pe) { } -// line 1714 "Parser.java" +// line 1679 "Parser.java" { cs = JSON_array_start; } -// line 753 "Parser.rl" +// line 718 "Parser.rl" -// line 1721 "Parser.java" +// line 1686 "Parser.java" { int _klen; int _trans = 0; @@ -1721,7 +1725,7 @@ else if ( _widec > _JSON_array_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 701 "Parser.rl" +// line 666 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -1791,7 +1795,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 703 "Parser.rl" +// line 668 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -1808,13 +1812,13 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) } break; case 1: -// line 718 "Parser.rl" +// line 683 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1857 "Parser.java" +// line 1822 "Parser.java" } } } @@ -1834,7 +1838,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) break; } } -// line 754 "Parser.rl" +// line 719 "Parser.rl" if (cs >= JSON_array_first_final) { res.update(result, p + 1); @@ -1844,7 +1848,7 @@ else if ( _widec > _JSON_array_trans_keys[_mid+1] ) } -// line 1887 "Parser.java" +// line 1852 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -2021,7 +2025,7 @@ private static byte[] init__JSON_object_trans_actions_0() static final int JSON_object_en_main = 1; -// line 815 "Parser.rl" +// line 780 "Parser.rl" void parseObject(ThreadContext context, ParserResult res, int p, int pe) { @@ -2046,14 +2050,14 @@ void parseObject(ThreadContext context, ParserResult res, int p, int pe) { } -// line 2089 "Parser.java" +// line 2054 "Parser.java" { cs = JSON_object_start; } -// line 839 "Parser.rl" +// line 804 "Parser.rl" -// line 2096 "Parser.java" +// line 2061 "Parser.java" { int _klen; int _trans = 0; @@ -2096,7 +2100,7 @@ else if ( _widec > _JSON_object_cond_keys[_mid+1] ) case 0: { _widec = 65536 + (data[p] - 0); if ( -// line 768 "Parser.rl" +// line 733 "Parser.rl" parser.allowTrailingComma ) _widec += 65536; break; } @@ -2166,7 +2170,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 770 "Parser.rl" +// line 735 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -2183,7 +2187,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } break; case 1: -// line 785 "Parser.rl" +// line 750 "Parser.rl" { parseString(context, res, p, pe); if (res.result == null) { @@ -2201,13 +2205,13 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } break; case 2: -// line 801 "Parser.rl" +// line 766 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 2250 "Parser.java" +// line 2215 "Parser.java" } } } @@ -2227,7 +2231,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) break; } } -// line 840 "Parser.rl" +// line 805 "Parser.rl" if (cs < JSON_object_first_final) { res.update(null, p + 1); @@ -2263,7 +2267,7 @@ else if ( _widec > _JSON_object_trans_keys[_mid+1] ) } -// line 2306 "Parser.java" +// line 2271 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { @@ -2366,7 +2370,7 @@ private static byte[] init__JSON_trans_actions_0() static final int JSON_en_main = 1; -// line 894 "Parser.rl" +// line 859 "Parser.rl" public IRubyObject parseImplementation(ThreadContext context) { @@ -2376,16 +2380,16 @@ public IRubyObject parseImplementation(ThreadContext context) { ParserResult res = new ParserResult(); -// line 2419 "Parser.java" +// line 2384 "Parser.java" { cs = JSON_start; } -// line 903 "Parser.rl" +// line 868 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2428 "Parser.java" +// line 2393 "Parser.java" { int _klen; int _trans = 0; @@ -2466,7 +2470,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) switch ( _JSON_actions[_acts++] ) { case 0: -// line 880 "Parser.rl" +// line 845 "Parser.rl" { parseValue(context, res, p, pe); if (res.result == null) { @@ -2478,7 +2482,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) } } break; -// line 2521 "Parser.java" +// line 2486 "Parser.java" } } } @@ -2498,7 +2502,7 @@ else if ( data[p] > _JSON_trans_keys[_mid+1] ) break; } } -// line 906 "Parser.rl" +// line 871 "Parser.rl" if (cs >= JSON_first_final && p == pe) { return result; diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index f40b94e2..bf42b445 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -5,10 +5,12 @@ */ package json.ext; +import org.jcodings.Encoding; +import org.jcodings.specific.ASCIIEncoding; +import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyClass; -import org.jruby.RubyEncoding; import org.jruby.RubyFloat; import org.jruby.RubyHash; import org.jruby.RubyInteger; @@ -236,12 +238,13 @@ public class Parser extends RubyObject { * Returns the source string if no conversion is needed. */ private RubyString convertEncoding(ThreadContext context, RubyString source) { - RubyEncoding encoding = (RubyEncoding)source.encoding(context); - if (encoding == info.ascii8bit.get()) { + Encoding encoding = source.getEncoding(); + if (encoding == ASCIIEncoding.INSTANCE) { source = (RubyString) source.dup(); - source.force_encoding(context, info.utf8.get()); - } else { - source = (RubyString) source.encode(context, info.utf8.get()); + source.setEncoding(UTF8Encoding.INSTANCE); + source.clearCodeRange(); + } else if (encoding != UTF8Encoding.INSTANCE) { + source = (RubyString) source.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } return source; } @@ -639,7 +642,8 @@ public class Parser extends RubyObject { if (cs >= JSON_string_first_final && result != null) { if (result instanceof RubyString) { RubyString string = (RubyString)result; - string.force_encoding(context, info.utf8.get()); + string.setEncoding(UTF8Encoding.INSTANCE); + string.clearCodeRange(); if (parser.freeze) { string.setFrozen(true); string = context.runtime.freezeAndDedupString(string); diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java index fcfaee3e..f430a64c 100644 --- a/java/src/json/ext/RuntimeInfo.java +++ b/java/src/json/ext/RuntimeInfo.java @@ -10,7 +10,6 @@ import java.util.WeakHashMap; import org.jruby.Ruby; import org.jruby.RubyClass; -import org.jruby.RubyEncoding; import org.jruby.RubyModule; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; @@ -36,21 +35,7 @@ final class RuntimeInfo { /** JSON::SAFE_STATE_PROTOTYPE */ WeakReference safeStatePrototype; - final WeakReference utf8; - final WeakReference ascii8bit; - - private RuntimeInfo(Ruby runtime) { - RubyClass encodingClass = runtime.getEncoding(); - if (encodingClass == null) { // 1.8 mode - utf8 = ascii8bit = null; - } else { - ThreadContext context = runtime.getCurrentContext(); - - utf8 = new WeakReference<>((RubyEncoding) RubyEncoding.find(context, - encodingClass, runtime.newString("utf-8"))); - ascii8bit = new WeakReference<>((RubyEncoding) RubyEncoding.find(context, - encodingClass, runtime.newString("ascii-8bit"))); - } + private RuntimeInfo() { } static RuntimeInfo initRuntime(Ruby runtime) { @@ -59,7 +44,7 @@ static RuntimeInfo initRuntime(Ruby runtime) { return info1; } else if (runtime1.get() == null) { runtime1 = new WeakReference<>(runtime); - info1 = new RuntimeInfo(runtime); + info1 = new RuntimeInfo(); return info1; } else { if (runtimes == null) { @@ -67,7 +52,7 @@ static RuntimeInfo initRuntime(Ruby runtime) { } RuntimeInfo cache = runtimes.get(runtime); if (cache == null) { - cache = new RuntimeInfo(runtime); + cache = new RuntimeInfo(); runtimes.put(runtime, cache); } return cache; From d798d799e7f5ff907730596510acf3ac0ae8ee27 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 21 Nov 2024 17:21:41 -0600 Subject: [PATCH 11/15] Eliminate String::Extend weak reference Rarely used, mostly at boot, and probably just as quick to look up the constant as go through the weak reference. --- java/src/json/ext/GeneratorMethods.java | 8 ++++---- java/src/json/ext/RuntimeInfo.java | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java index 81f01162..eb31b9ef 100644 --- a/java/src/json/ext/GeneratorMethods.java +++ b/java/src/json/ext/GeneratorMethods.java @@ -44,8 +44,8 @@ static void populate(RuntimeInfo info, RubyModule module) { defineMethods(module, "String", RbString.class); defineMethods(module, "TrueClass", RbTrue.class); - info.stringExtendModule = new WeakReference<>(module.defineModuleUnder("String").defineModuleUnder("Extend")); - info.stringExtendModule.get().defineAnnotatedMethods(StringExtend.class); + RubyModule stringExtend = module.defineModuleUnder("String").defineModuleUnder("Extend"); + stringExtend.defineAnnotatedMethods(StringExtend.class); } /** @@ -171,9 +171,9 @@ private static RubyHash toJsonRawObject(ThreadContext context, } @JRubyMethod(module=true) - public static IRubyObject included(ThreadContext context, IRubyObject vSelf, IRubyObject module) { + public static IRubyObject included(ThreadContext context, IRubyObject extendModule, IRubyObject module) { RuntimeInfo info = RuntimeInfo.forRuntime(context.runtime); - return module.callMethod(context, "extend", info.stringExtendModule.get()); + return module.callMethod(context, "extend", ((RubyModule) extendModule).getConstant("Extend")); } } diff --git a/java/src/json/ext/RuntimeInfo.java b/java/src/json/ext/RuntimeInfo.java index f430a64c..f712dc25 100644 --- a/java/src/json/ext/RuntimeInfo.java +++ b/java/src/json/ext/RuntimeInfo.java @@ -28,8 +28,6 @@ final class RuntimeInfo { // the Ruby runtime object, which would cause memory leaks in the runtimes map above. /** JSON */ WeakReference jsonModule; - /** JSON::Ext::Generator::GeneratorMethods::String::Extend */ - WeakReference stringExtendModule; /** JSON::Ext::Generator::State */ WeakReference generatorStateClass; /** JSON::SAFE_STATE_PROTOTYPE */ From 5b77eed4a67bd9ac4987b0518e7d060a64d62686 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 22 Nov 2024 09:09:54 -0600 Subject: [PATCH 12/15] Write bytes directly --- java/src/json/ext/ByteListTranscoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index dc294f05..41e4e393 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -141,7 +141,7 @@ protected void quoteStart() { */ protected void quoteStop(int endPos) throws IOException { if (quoteStart != -1) { - out.write(src.bytes(), quoteStart, endPos - quoteStart); + out.write(src.unsafeBytes(), src.begin() + quoteStart, endPos - quoteStart); quoteStart = -1; } } From f30ade0fbcdd91a7b981c6c2b998899dcc0cd2ab Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 22 Nov 2024 09:13:37 -0600 Subject: [PATCH 13/15] Duplicate to_s shortcut from CRuby --- java/src/json/ext/Generator.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index 6db539a3..a502b7c9 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -11,10 +11,12 @@ import org.jruby.RubyBasicObject; import org.jruby.RubyBignum; import org.jruby.RubyBoolean; +import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyFloat; import org.jruby.RubyHash; import org.jruby.RubyString; +import org.jruby.RubySymbol; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; @@ -22,6 +24,7 @@ import org.jruby.exceptions.RaiseException; import org.jruby.util.ConvertBytes; import org.jruby.util.IOOutputStream; +import org.jruby.util.TypeConverter; import java.io.BufferedOutputStream; import java.io.IOException; @@ -366,7 +369,20 @@ public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyOb Ruby runtime = context.runtime; - IRubyObject keyStr = key.callMethod(context, "to_s"); + IRubyObject keyStr; + RubyClass keyClass = key.getType(); + if (key instanceof RubyString) { + if (keyClass == runtime.getString()) { + keyStr = key; + } else { + keyStr = key.callMethod(context, "to_s"); + } + } else if (keyClass == runtime.getSymbol()) { + keyStr = key.asString(); + } else { + keyStr = TypeConverter.convertToType(key, runtime.getString(), "to_s"); + } + if (keyStr.getMetaClass() == runtime.getString()) { STRING_HANDLER.generate(context, session, (RubyString) keyStr, buffer); } else { From 6d852f175072bb02c986b4cca3260b6b7d66bd86 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 22 Nov 2024 11:08:19 -0600 Subject: [PATCH 14/15] Duplicate CRuby handling of UTF-8 compatible strings Previous logic would only pass the string through if its encoding was exactly UTF-8. New logic passes through UTF-8 and US-ASCII and handles other encodings like CRuby. --- java/src/json/ext/ByteListTranscoder.java | 5 +++ java/src/json/ext/Generator.java | 47 +++++++++++++++++++---- java/src/json/ext/StringEncoder.java | 10 +++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index 41e4e393..883cb407 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -104,6 +104,11 @@ protected int readUtf8Char(ThreadContext context) { throw invalidUtf8(context); } + protected int readASCIIChar() { + charStart = pos; + return next(); + } + /** * Throws a GeneratorError if the input list doesn't have at least this * many bytes left. diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index a502b7c9..9f5f7e86 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -5,6 +5,9 @@ */ package json.ext; +import org.jcodings.Encoding; +import org.jcodings.specific.ASCIIEncoding; +import org.jcodings.specific.USASCIIEncoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyArray; @@ -24,6 +27,7 @@ import org.jruby.exceptions.RaiseException; import org.jruby.util.ConvertBytes; import org.jruby.util.IOOutputStream; +import org.jruby.util.StringSupport; import org.jruby.util.TypeConverter; import java.io.BufferedOutputStream; @@ -423,22 +427,49 @@ int guessSize(ThreadContext context, Session session, RubyString object) { @Override void generate(ThreadContext context, Session session, RubyString object, OutputStream buffer) throws IOException { - RubyString src; - try { - if (object.getEncoding() != UTF8Encoding.INSTANCE) { - src = (RubyString)object.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); - } else { - src = object; + object = ensureValidEncoding(context, object); + StringEncoder stringEncoder = session.getStringEncoder(context); + ByteList byteList = object.getByteList(); + switch (object.scanForCodeRange()) { + case StringSupport.CR_7BIT: + stringEncoder.encodeASCII(context, byteList, buffer); + break; + case StringSupport.CR_VALID: + stringEncoder.encode(context, byteList, buffer); + break; + default: + throw stringEncoder.invalidUtf8(context); } - - session.getStringEncoder(context).encode(context, src.getByteList(), buffer); } catch (RaiseException re) { throw Utils.newException(context, Utils.M_GENERATOR_ERROR, re.getMessage()); } } }; + static RubyString ensureValidEncoding(ThreadContext context, RubyString str) { + Encoding encoding = str.getEncoding(); + RubyString utf8String; + if (!(encoding == USASCIIEncoding.INSTANCE || encoding == UTF8Encoding.INSTANCE)) { + if (encoding == ASCIIEncoding.INSTANCE) { + utf8String = str.strDup(context.runtime); + utf8String.setEncoding(UTF8Encoding.INSTANCE); + switch (utf8String.getCodeRange()) { + case StringSupport.CR_7BIT: + return utf8String; + case StringSupport.CR_VALID: + // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. + // TODO: Raise in 3.0.0 + context.runtime.getWarnings().warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); + return utf8String; + } + } + + str = (RubyString) str.encode(context, context.runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); + } + return str; + } + static final Handler TRUE_HANDLER = new KeywordHandler<>("true"); static final Handler FALSE_HANDLER = diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index 8d23dfb9..9ffb9043 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -55,6 +55,16 @@ void encode(ThreadContext context, ByteList src, OutputStream out) throws IOExce append('"'); } + void encodeASCII(ThreadContext context, ByteList src, OutputStream out) throws IOException { + init(src, out); + append('"'); + while (hasNext()) { + handleChar(readASCIIChar()); + } + quoteStop(pos); + append('"'); + } + private void handleChar(int c) throws IOException { switch (c) { case '"': From 0ce2119592ecce9ee3731149748442ad995667ba Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 22 Nov 2024 14:32:02 -0600 Subject: [PATCH 15/15] Restore direct-to-ByteList for decoding String Using an output stream here is just added overhead compared to writing directly to a ByteList. Instead, we move the OutputStream logic into StringEncoder and restore direct ByteList writing to StringDecoder. --- java/src/json/ext/ByteListTranscoder.java | 18 ++++++------------ java/src/json/ext/StringDecoder.java | 16 +++++++++++++--- java/src/json/ext/StringEncoder.java | 16 ++++++++++++++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/java/src/json/ext/ByteListTranscoder.java b/java/src/json/ext/ByteListTranscoder.java index 883cb407..78d8037c 100644 --- a/java/src/json/ext/ByteListTranscoder.java +++ b/java/src/json/ext/ByteListTranscoder.java @@ -24,7 +24,6 @@ abstract class ByteListTranscoder { /** Position of the next character to read */ protected int pos; - private OutputStream out; /** * When a character that can be copied straight into the output is found, * its index is stored on this variable, and copying is delayed until @@ -34,16 +33,15 @@ abstract class ByteListTranscoder { */ private int quoteStart = -1; - protected void init(ByteList src, OutputStream out) { - this.init(src, 0, src.length(), out); + protected void init(ByteList src) { + this.init(src, 0, src.length()); } - protected void init(ByteList src, int start, int end, OutputStream out) { + protected void init(ByteList src, int start, int end) { this.src = src; this.pos = start; this.charStart = start; this.srcEnd = end; - this.out = out; } /** @@ -146,18 +144,14 @@ protected void quoteStart() { */ protected void quoteStop(int endPos) throws IOException { if (quoteStart != -1) { - out.write(src.unsafeBytes(), src.begin() + quoteStart, endPos - quoteStart); + append(src.unsafeBytes(), src.begin() + quoteStart, endPos - quoteStart); quoteStart = -1; } } - protected void append(int b) throws IOException { - out.write(b); - } + protected abstract void append(int b) throws IOException; - protected void append(byte[] origin, int start, int length) throws IOException { - out.write(origin, start, length); - } + protected abstract void append(byte[] origin, int start, int length) throws IOException; protected abstract RaiseException invalidUtf8(ThreadContext context); diff --git a/java/src/json/ext/StringDecoder.java b/java/src/json/ext/StringDecoder.java index ff984935..bf616c1e 100644 --- a/java/src/json/ext/StringDecoder.java +++ b/java/src/json/ext/StringDecoder.java @@ -23,18 +23,20 @@ final class StringDecoder extends ByteListTranscoder { */ private int surrogatePairStart = -1; + private ByteList out; + // Array used for writing multibyte characters into the buffer at once private final byte[] aux = new byte[4]; ByteList decode(ThreadContext context, ByteList src, int start, int end) { try { - ByteListDirectOutputStream out = new ByteListDirectOutputStream(end - start); - init(src, start, end, out); + init(src, start, end); + this.out = new ByteList(end - start); while (hasNext()) { handleChar(context, readUtf8Char(context)); } quoteStop(pos); - return out.toByteListDirect(src.getEncoding()); + return out; } catch (IOException e) { throw context.runtime.newIOErrorFromException(e); } @@ -84,6 +86,14 @@ private void handleEscapeSequence(ThreadContext context) throws IOException { } } + protected void append(int b) throws IOException { + out.append(b); + } + + protected void append(byte[] origin, int start, int length) throws IOException { + out.append(origin, start, length); + } + private void handleLowSurrogate(ThreadContext context, char highSurrogate) throws IOException { surrogatePairStart = charStart; ensureMin(context, 1); diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index 9ffb9043..63df459d 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -20,6 +20,8 @@ final class StringEncoder extends ByteListTranscoder { private final boolean asciiOnly, scriptSafe; + private OutputStream out; + // Escaped characters will reuse this array, to avoid new allocations // or appending them byte-by-byte private final byte[] aux = @@ -46,7 +48,8 @@ final class StringEncoder extends ByteListTranscoder { } void encode(ThreadContext context, ByteList src, OutputStream out) throws IOException { - init(src, out); + init(src); + this.out = out; append('"'); while (hasNext()) { handleChar(readUtf8Char(context)); @@ -56,7 +59,8 @@ void encode(ThreadContext context, ByteList src, OutputStream out) throws IOExce } void encodeASCII(ThreadContext context, ByteList src, OutputStream out) throws IOException { - init(src, out); + init(src); + this.out = out; append('"'); while (hasNext()) { handleChar(readASCIIChar()); @@ -65,6 +69,14 @@ void encodeASCII(ThreadContext context, ByteList src, OutputStream out) throws I append('"'); } + protected void append(int b) throws IOException { + out.write(b); + } + + protected void append(byte[] origin, int start, int length) throws IOException { + out.write(origin, start, length); + } + private void handleChar(int c) throws IOException { switch (c) { case '"':