On Tue, Nov 3, 2026 at 5:22 PM Mark Thomas via dev
<[email protected]> wrote:
>
> On 03/11/2026 14:55, Mark Thomas via dev wrote:
> > On 03/11/2026 14:51, markt.apache.org via dev wrote:
> >> This is an automated email from the ASF dual-hosted git repository.
> >>
> >> markt-asf pushed a commit to branch main
> >> in repository https://gitbox.apache.org/repos/asf/tomcat.git
> >>
> >>
> >> The following commit(s) were added to refs/heads/main by this push:
> >> new 221d759137 Review "Parse exponent-only numbers in the JSON
> >> parser"
> >> 221d759137 is described below
> >>
> >> commit 221d7591378bc50f6eebbb172e75b8386d7f69c0
> >> Author: Mark Thomas <[email protected]>
> >> AuthorDate: Tue Nov 3 14:51:15 2026 +0000
> >>
> >> Review "Parse exponent-only numbers in the JSON parser"
> >> Describe behaviour change in changelog
> >> Treat out of range number as a ParseException
> >
> > The AI chnaged the generated code. I still need to confirm that JavaCC
> > produces the same result.
>
> Close enough (JavaCC adds various odds and end that are all effectvely
> NO-OPs) that I'm not going to update the generated code.
When my model hacks this, it usually also uses javacc on its own to
verify the added code is equivalent. I think this is nice since it
works and it does not ruin the formatting/cleanups.
Rémy
> Mark
>
> >
> > Mark
> >
> >> ---
> >> java/org/apache/tomcat/util/json/JSONParser.java | 12 ++++--
> >> java/org/apache/tomcat/util/json/JSONParser.jjt | 8 +++-
> >> .../apache/tomcat/util/json/TestJSONParser.java | 48 ++++++++++++
> >> ++++++++++
> >> webapps/docs/changelog.xml | 17 ++++++++
> >> 4 files changed, 81 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/java/org/apache/tomcat/util/json/JSONParser.java b/java/
> >> org/apache/tomcat/util/json/JSONParser.java
> >> index 534692eb20..7abfab60a4 100644
> >> --- a/java/org/apache/tomcat/util/json/JSONParser.java
> >> +++ b/java/org/apache/tomcat/util/json/JSONParser.java
> >> @@ -448,10 +448,16 @@ public class JSONParser implements
> >> JSONParserConstants {
> >> }
> >> }
> >> } else {
> >> - {
> >> - if ("" != null) {
> >> - return new java.math.BigDecimal(t.image);
> >> + try {
> >> + {
> >> + if ("" != null) {
> >> + return new
> >> java.math.BigDecimal(t.image);
> >> + }
> >> }
> >> + } catch (NumberFormatException nfe) {
> >> + // The grammar admits exponents that
> >> BigDecimal can't represent. Report them as a parse
> >> + // error rather than letting an unchecked
> >> exception escape the parser.
> >> + throw new ParseException("Exponent out of
> >> range in number [" + t.image + "]");
> >> }
> >> }
> >> break;
> >> diff --git a/java/org/apache/tomcat/util/json/JSONParser.jjt b/java/
> >> org/apache/tomcat/util/json/JSONParser.jjt
> >> index c50f41935a..89c05f3ae8 100644
> >> --- a/java/org/apache/tomcat/util/json/JSONParser.jjt
> >> +++ b/java/org/apache/tomcat/util/json/JSONParser.jjt
> >> @@ -413,7 +413,13 @@ Number number(): {
> >> if (nativeNumbers) {
> >> return Double.valueOf(t.image);
> >> } else {
> >> - return new java.math.BigDecimal(t.image);
> >> + try {
> >> + return new java.math.BigDecimal(t.image);
> >> + } catch (NumberFormatException nfe) {
> >> + // The grammar admits exponents that BigDecimal
> >> can't represent. Report them as a parse error
> >> + // rather than letting an unchecked exception
> >> escape the parser.
> >> + throw new ParseException("Exponent out of range
> >> in number [" + t.image + "]");
> >> + }
> >> }
> >> }
> >> ) | (
> >> diff --git a/test/org/apache/tomcat/util/json/TestJSONParser.java b/
> >> test/org/apache/tomcat/util/json/TestJSONParser.java
> >> index 9771bbf0bb..47f0588cb8 100644
> >> --- a/test/org/apache/tomcat/util/json/TestJSONParser.java
> >> +++ b/test/org/apache/tomcat/util/json/TestJSONParser.java
> >> @@ -111,6 +111,54 @@ public class TestJSONParser {
> >> Assert.assertEquals(BigInteger.ONE, result.get("1E+5"));
> >> }
> >> + /*
> >> + * The exponent of a number must be one that BigDecimal can
> >> represent. A larger exponent must be reported as a
> >> + * ParseException rather than as an unchecked NumberFormatException.
> >> + */
> >> + @Test
> >> + public void testExponentOutOfRangeIsParseException() {
> >> + for (String number : new String[] { "1e-2147483649",
> >> "1e2147483649", "1.5e-2147483649", "-1E2147483649" }) {
> >> + try {
> >> + new JSONParser("[" + number + "]").parse();
> >> + Assert.fail("A ParseException was expected for " +
> >> number);
> >> + } catch (ParseException e) {
> >> + Assert.assertTrue(e.getMessage(),
> >> e.getMessage().contains(number));
> >> + }
> >> + }
> >> + }
> >> +
> >> + @Test
> >> + public void testExponentOutOfRangeAsKeyIsParseException() {
> >> + Assert.assertThrows(ParseException.class, () -> new
> >> JSONParser("{1e2147483649:1}").parseObject());
> >> + }
> >> +
> >> + @Test
> >> + public void testLargestExponentsAccepted() throws ParseException {
> >> + JSONParser parser = new JSONParser("[1e2147483647,
> >> 1e-2147483647]");
> >> + List<Object> values = parser.parseArray();
> >> + Assert.assertEquals(new BigDecimal("1e2147483647"),
> >> values.get(0));
> >> + Assert.assertEquals(new BigDecimal("1e-2147483647"),
> >> values.get(1));
> >> + }
> >> +
> >> + @Test
> >> + public void testExponentWithLeadingZeros() throws ParseException {
> >> + Assert.assertEquals(new BigDecimal("1E5"), new
> >> JSONParser("1e05").parse());
> >> + }
> >> +
> >> + /*
> >> + * Bare words that start with a number but are not numbers are
> >> not affected by the exponent-only number syntax.
> >> + */
> >> + @Test
> >> + public void testBareWordKeysStartingWithNumber() throws
> >> ParseException {
> >> + Map<String, Object> result = new JSONParser("{1e5x:1,
> >> 12abc:2, 1e:3, 3E2:4}").parseObject();
> >> + Assert.assertEquals(BigInteger.valueOf(1), result.get("1e5x"));
> >> + Assert.assertEquals(BigInteger.valueOf(2), result.get("12abc"));
> >> + Assert.assertEquals(BigInteger.valueOf(3), result.get("1e"));
> >> + // An exponent-only number is a number, so its key is the
> >> normalised form of the number
> >> + Assert.assertEquals(BigInteger.valueOf(4), result.get("3E+2"));
> >> + Assert.assertEquals(4, result.size());
> >> + }
> >> +
> >> private static String nest(int depth) {
> >> StringBuilder result = new StringBuilder(depth * 2);
> >> for (int i = 0; i < depth; i++) {
> >> diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
> >> index 7c0def85ae..27de6fa631 100644
> >> --- a/webapps/docs/changelog.xml
> >> +++ b/webapps/docs/changelog.xml
> >> @@ -613,6 +613,23 @@
> >> closes the entire connection. These frames are already
> >> permitted when
> >> the stream is closed in the other direction. (remm)
> >> </fix>
> >> + <fix>
> >> + The JSON parser now accepts numbers that have an exponent but no
> >> + fraction, such as <code>1e5</code>, as required by RFC 8259.
> >> Note that
> >> + when the lenient dialect is used, a bare object key that is
> >> such a
> >> + number (for example <code>{1e5:1}</code>) is now a number and
> >> the key
> >> + is the string form of the parsed number (<code>1E+5</code>)
> >> rather than
> >> + the original text, as already happened for bare keys that
> >> have both a
> >> + fraction and an exponent. Quoted keys are not affected.
> >> Numbers with an
> >> + exponent that is too large or too small to be represented are
> >> now
> >> + rejected with a <code>ParseException</code> rather than an
> >> unchecked
> >> + <code>NumberFormatException</code>. (remm)
> >> + </fix>
> >> + <fix>
> >> + With native numbers enabled, the JSON parser now returns
> >> integers that
> >> + do not fit in a <code>long</code> as <code>BigInteger</code>
> >> rather than
> >> + failing with an unchecked <code>NumberFormatException</code>.
> >> (remm)
> >> + </fix>
> >> <fix>
> >> The JSON parser now rejects input with a nesting depth
> >> greater than
> >> 1000 with a <code>ParseException</code>. Previously, deeply
> >> nested
> >>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: [email protected]
> >> For additional commands, e-mail: [email protected]
> >>
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: [email protected]
> > For additional commands, e-mail: [email protected]
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]