You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/09/01 11:10:58 UTC

(grails-core) branch fix/json-scanner-number-parsing created (now 070707e354)

This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a change to branch fix/json-scanner-number-parsing
in repository https://gitbox.apache.org/repos/asf/grails-core.git


      at 070707e354 Fix JsonScanner number parsing for bare exponents and -Infinity

This branch includes the following new commits:

     new 070707e354 Fix JsonScanner number parsing for bare exponents and -Infinity

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(grails-core) 01/01: Fix JsonScanner number parsing for bare exponents and -Infinity

Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch fix/json-scanner-number-parsing
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 070707e354fbbe82aab511b5786371f27577cef2
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sun Aug 30 19:17:58 2026 -0500

    Fix JsonScanner number parsing for bare exponents and -Infinity
    
    Two bugs found while adding test coverage for JsonReader/JsonWriter on
    8.1.x, cherry-picked here since JsonScanner is unchanged since 7.0.x:
    
    - SAW_EXPONENT_DIGITS was the only numeric scanner state whose
      terminator switch omitted an explicit end-of-input case (every
      sibling state treats EOF the same as a closing delimiter), so a
      bare exponent-notation number with nothing following it (e.g. a
      top-level "1e2") threw JsonParseException instead of parsing.
    
    - SAW_MINUS_I appended the character it read on every iteration of
      its "-Infinity" match loop, including the terminator character read
      immediately after matching the literal's final 'y' - so the buffer
      handed to Double.parseDouble always carried one extra trailing
      character, and "-Infinity" failed to parse in every context
      (end-of-input, before a comma, before a closing bracket).
    
    This branch has no prior JsonReader test coverage, so a minimal
    JsonReaderSpec is added here with just the two regression tests -
    broader JsonReader/JsonWriter coverage exists only on 8.1.x.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01QAjfckcY4EJsxranv1RyGV
---
 .../grails/datastore/bson/json/JsonScanner.java    |  5 +-
 .../datastore/bson/json/JsonReaderSpec.groovy      | 62 ++++++++++++++++++++++
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/grails-data-mongodb/bson/src/main/groovy/org/grails/datastore/bson/json/JsonScanner.java b/grails-data-mongodb/bson/src/main/groovy/org/grails/datastore/bson/json/JsonScanner.java
index ea55f0cd38..de1fc55ee9 100644
--- a/grails-data-mongodb/bson/src/main/groovy/org/grails/datastore/bson/json/JsonScanner.java
+++ b/grails-data-mongodb/bson/src/main/groovy/org/grails/datastore/bson/json/JsonScanner.java
@@ -361,6 +361,7 @@ class JsonScanner {
                         case JsonToken.CLOSE_BRACE:
                         case JsonToken.CLOSE_BRACKET:
                         case JsonToken.CLOSE_PARENS:
+                        case -1:
                             state = JsonScanner.NumberState.DONE;
                             break;
                         default:
@@ -384,7 +385,9 @@ class JsonScanner {
                             break;
                         }
                         c = readCharacter();
-                        numberBuilder.append((char) c);
+                        if (i < NFINITY.length - 1) {
+                            numberBuilder.append((char) c);
+                        }
                     }
                     if (sawMinusInfinity) {
                         type = JsonTokenType.DOUBLE;
diff --git a/grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/json/JsonReaderSpec.groovy b/grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/json/JsonReaderSpec.groovy
new file mode 100644
index 0000000000..16b1b0fb6b
--- /dev/null
+++ b/grails-data-mongodb/bson/src/test/groovy/org/grails/datastore/bson/json/JsonReaderSpec.groovy
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.bson.json
+
+import org.bson.BsonType
+import spock.lang.Specification
+
+/**
+ * Regression coverage for two JsonScanner number-parsing bugs:
+ *
+ * <p>SAW_EXPONENT_DIGITS was the only numeric scanner state whose terminator switch omitted an
+ * explicit end-of-input case (every sibling state treats EOF the same as a closing delimiter), so
+ * a bare exponent-notation number with nothing following it (e.g. a top-level "1e2") threw
+ * JsonParseException instead of parsing.
+ *
+ * <p>SAW_MINUS_I appended the character it read on every iteration of its "-Infinity" match loop,
+ * including the terminator character read immediately after matching the literal's final 'y' - so
+ * the buffer handed to Double.parseDouble always carried one extra trailing character, and
+ * "-Infinity" failed to parse in every context (end-of-input, before a comma, before a closing
+ * bracket).
+ */
+class JsonReaderSpec extends Specification {
+
+    void "reads a bare exponent-notation number with nothing following it"() {
+        given:
+        JsonReader reader = new JsonReader('1e2')
+
+        expect:
+        reader.readBsonType() == BsonType.DOUBLE
+        reader.readDouble() == 100.0d
+    }
+
+    void "reads -Infinity as negative infinity, at end-of-input and followed by a delimiter"() {
+        expect:
+        new JsonReader('-Infinity').readBsonType() == BsonType.DOUBLE
+
+        when:
+        JsonReader reader = new JsonReader('[-Infinity]')
+        reader.readBsonType()
+        reader.readStartArray()
+
+        then:
+        reader.readBsonType() == BsonType.DOUBLE
+        reader.readDouble() == Double.NEGATIVE_INFINITY
+    }
+}