From d67ecedbdb80b80b4ec15c7487f195180ab09f9c Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 24 Jun 2026 12:46:04 +0800 Subject: [PATCH] fix: std.parseJson handles integers outside Java long range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: std.parseJson crashed with NumberFormatException when parsing JSON integers that exceed Java long range ([-2^63, 2^63-1]). C++ jsonnet, go-jsonnet, and jrsonnet all gracefully parse them as float64. Modification: - ValVisitor.scala: Catch NumberFormatException from parseIntegralNum and fall back to Double.parseDouble, which handles arbitrarily large integers with float64 precision. Result: std.parseJson("9223372036854775808") now returns a float64 value instead of crashing. Cross-implementation comparison: | Expression | cpp-jsonnet | go-jsonnet | jrsonnet | sjsonnet (before) | sjsonnet (after) | |---|---|---|---|---|---| | parseJson("2^63") | 9223372036854775808 | 9223372036854775808 | 9.22e+18 | CRASH ❌ | 9.22e+18 ✅ | | parseJson("-2^63-1") | -9223372036854775808 | -9223372036854775808 | -9.22e+18 | CRASH ❌ | -9.22e+18 ✅ | | parseJson("10^31") | 10^31 (float) | 10^31 (float) | 10^31 (float) | CRASH ❌ | 10^31 (float) ✅ | | parseJson("42") | 42 | 42 | 42 | 42 ✅ | 42 ✅ | --- sjsonnet/src/sjsonnet/ValVisitor.scala | 5 ++++- .../new_test_suite/parsejson_large_integer.jsonnet | 12 ++++++++++++ .../parsejson_large_integer.jsonnet.golden | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet create mode 100644 sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet.golden diff --git a/sjsonnet/src/sjsonnet/ValVisitor.scala b/sjsonnet/src/sjsonnet/ValVisitor.scala index 5eec39014..33eda06c6 100644 --- a/sjsonnet/src/sjsonnet/ValVisitor.scala +++ b/sjsonnet/src/sjsonnet/ValVisitor.scala @@ -46,7 +46,10 @@ class ValVisitor(pos: Position) extends JsVisitor[Val, Val] { self => pos, if (decIndex != -1 || expIndex != -1) s.toString.toDouble else if (s.length() == 2 && s.charAt(0) == '-' && s.charAt(1) == '0') -0.0 - else upickle.core.ParseUtils.parseIntegralNum(s, decIndex, expIndex, index).toDouble + else { + try upickle.core.ParseUtils.parseIntegralNum(s, decIndex, expIndex, index).toDouble + catch { case _: NumberFormatException => s.toString.toDouble } + } ) def visitString(s: CharSequence, index: Int): Val = Val.Str(pos, s.toString) diff --git a/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet b/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet new file mode 100644 index 000000000..07912a293 --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet @@ -0,0 +1,12 @@ +// Regression test: std.parseJson must handle integers outside Java long range. +// Verified against cpp-jsonnet 0.21.0, go-jsonnet 0.22.0, jrsonnet 0.5.0-pre99. +// These used to crash with NumberFormatException; now they parse as float64. +std.parseJson("9223372036854775808") > 0 && +std.parseJson("-9223372036854775809") < 0 && +std.parseJson("99999999999999999999999999999999") > 0 && +// Normal integers still work +std.parseJson("42") == 42 && +std.parseJson("-1") == -1 && +std.parseJson("0") == 0 && +std.parseJson("-0") == 0 && +true diff --git a/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet.golden b/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet.golden new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/parsejson_large_integer.jsonnet.golden @@ -0,0 +1 @@ +true