diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2484.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2484.cs
index baa70f32..d779bb18 100644
--- a/Src/Newtonsoft.Json.Tests/Issues/Issue2484.cs
+++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2484.cs
@@ -23,7 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
-#if (NET45 || NET50)
+#if (NET45 || NET5_0_OR_GREATER)
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2492.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2492.cs
index 70268abf..34146a66 100644
--- a/Src/Newtonsoft.Json.Tests/Issues/Issue2492.cs
+++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2492.cs
@@ -23,7 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
-#if (NET45 || NET50)
+#if (NET45 || NET5_0_OR_GREATER)
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs
index 4363a911..9ff3be6e 100644
--- a/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs
+++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs
@@ -23,7 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
-#if (NET45 || NET50)
+#if (NET45 || NET5_0_OR_GREATER)
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2529.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2529.cs
index 5d7cb7b9..8406bfc0 100644
--- a/Src/Newtonsoft.Json.Tests/Issues/Issue2529.cs
+++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2529.cs
@@ -23,7 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
-#if (NET45 || NET50)
+#if (NET45 || NET5_0_OR_GREATER)
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2638.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2638.cs
new file mode 100644
index 00000000..c32fe4c6
--- /dev/null
+++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2638.cs
@@ -0,0 +1,129 @@
+#region License
+// Copyright (c) 2022 James Newton-King
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+#endregion
+
+using Newtonsoft.Json.Linq;
+using System.Globalization;
+using Newtonsoft.Json.Tests.Documentation.Samples.Linq;
+
+#if DNXCORE50
+using Xunit;
+using Test = Xunit.FactAttribute;
+using Assert = Newtonsoft.Json.Tests.XUnitAssert;
+#else
+using NUnit.Framework;
+#endif
+
+namespace Newtonsoft.Json.Tests.Issues
+{
+ [TestFixture]
+ public class Issue2638
+ {
+ [Test]
+ public void DeserilizeUsesSharedBooleans()
+ {
+ Test(true);
+ Test(false);
+
+ static void Test(bool value)
+ {
+ var obj = (JObject)JToken.Parse(@"{""x"": XXX, ""y"": XXX}".Replace("XXX", value ? "true" : "false"));
+ var x = ((JValue)obj["x"]).Value;
+ var y = ((JValue)obj["y"]).Value;
+
+ Assert.AreEqual(value, (bool)x);
+ Assert.AreEqual(value, (bool)y);
+ Assert.AreSame(x, y);
+ }
+ }
+
+ [Test]
+ public void DeserilizeUsesSharedDoubleZeros()
+ {
+ Test(0, true);
+ Test(double.NaN, true);
+ Test(double.NegativeInfinity, true);
+ Test(double.PositiveInfinity, true);
+ Test(1, false);
+ Test(42.42, false);
+
+ static void Test(double value, bool expectSame)
+ {
+ var obj = (JObject)JToken.Parse(@"{""x"": XXX, ""y"": XXX}".Replace("XXX", value.ToString("0.0###", CultureInfo.InvariantCulture)));
+ var x = ((JValue)obj["x"]).Value;
+ var y = ((JValue)obj["y"]).Value;
+
+ Assert.AreEqual(value, (double)x);
+ Assert.AreEqual(value, (double)y);
+ if (expectSame)
+ {
+ Assert.AreSame(x, y);
+ }
+ else
+ {
+ Assert.AreNotSame(x, y);
+ }
+ var unboxed = (double)x;
+ Assert.AreEqual(double.IsNaN(value), double.IsNaN(unboxed));
+ Assert.AreEqual(double.IsPositiveInfinity(value), double.IsPositiveInfinity(unboxed));
+ Assert.AreEqual(double.IsNegativeInfinity(value), double.IsNegativeInfinity(unboxed));
+ }
+ }
+
+ [Test]
+ public void DeserilizeUsesSharedSmallInt64()
+ {
+ Test(-2, false);
+ Test(-1, true);
+ Test(0, true);
+ Test(1, true);
+ Test(2, true);
+ Test(3, true);
+ Test(4, true);
+ Test(5, true);
+ Test(6, true);
+ Test(7, true);
+ Test(8, true);
+ Test(9, false);
+
+ static void Test(long value, bool expectSame)
+ {
+ var obj = (JObject)JToken.Parse(@"{""x"": XXX, ""y"": XXX}".Replace("XXX", value.ToString(CultureInfo.InvariantCulture)));
+ var x = ((JValue)obj["x"]).Value;
+ var y = ((JValue)obj["y"]).Value;
+
+ Assert.AreEqual(value, (long)x);
+ Assert.AreEqual(value, (long)y);
+ if (expectSame)
+ {
+ Assert.AreSame(x, y);
+ }
+ else
+ {
+ Assert.AreNotSame(x, y);
+ }
+ }
+ }
+ }
+}
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
index 250b9b66..f1166b1a 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
@@ -13,7 +13,7 @@
Newtonsoft.Json.Tests
false
- Full
+ Full
false
diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs
index 0ef5b0e5..6b107b0e 100644
--- a/Src/Newtonsoft.Json/JsonTextReader.cs
+++ b/Src/Newtonsoft.Json/JsonTextReader.cs
@@ -841,7 +841,7 @@ namespace Newtonsoft.Json
{
throw CreateUnexpectedCharacterException(_chars[_charPos]);
}
- SetToken(JsonToken.Boolean, isTrue);
+ SetToken(JsonToken.Boolean, BoxedPrimitives.Get(isTrue));
return isTrue;
case '/':
ParseComment(false);
@@ -2030,7 +2030,7 @@ namespace Newtonsoft.Json
if (singleDigit)
{
// digit char values start at 48
- numberValue = firstChar - 48;
+ numberValue = BoxedPrimitives.Get(firstChar - 48);
}
else if (nonBase10)
{
@@ -2040,7 +2040,7 @@ namespace Newtonsoft.Json
{
int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(number, 16) : Convert.ToInt32(number, 8);
- numberValue = integer;
+ numberValue = BoxedPrimitives.Get(integer);
}
catch (Exception ex)
{
@@ -2052,7 +2052,7 @@ namespace Newtonsoft.Json
ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out int value);
if (parseResult == ParseResult.Success)
{
- numberValue = value;
+ numberValue = BoxedPrimitives.Get(value);
}
else if (parseResult == ParseResult.Overflow)
{
@@ -2072,7 +2072,7 @@ namespace Newtonsoft.Json
if (singleDigit)
{
// digit char values start at 48
- numberValue = (decimal)firstChar - 48;
+ numberValue = BoxedPrimitives.Get((decimal)firstChar - 48);
}
else if (nonBase10)
{
@@ -2083,7 +2083,7 @@ namespace Newtonsoft.Json
// decimal.Parse doesn't support parsing hexadecimal values
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
- numberValue = Convert.ToDecimal(integer);
+ numberValue = BoxedPrimitives.Get(Convert.ToDecimal(integer));
}
catch (Exception ex)
{
@@ -2095,7 +2095,7 @@ namespace Newtonsoft.Json
ParseResult parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal value);
if (parseResult == ParseResult.Success)
{
- numberValue = value;
+ numberValue = BoxedPrimitives.Get(value);
}
else
{
@@ -2111,7 +2111,7 @@ namespace Newtonsoft.Json
if (singleDigit)
{
// digit char values start at 48
- numberValue = (double)firstChar - 48;
+ numberValue = BoxedPrimitives.Get((double)firstChar - 48);
}
else if (nonBase10)
{
@@ -2122,7 +2122,7 @@ namespace Newtonsoft.Json
// double.Parse doesn't support parsing hexadecimal values
long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
- numberValue = Convert.ToDouble(integer);
+ numberValue = BoxedPrimitives.Get(Convert.ToDouble(integer));
}
catch (Exception ex)
{
@@ -2135,7 +2135,7 @@ namespace Newtonsoft.Json
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
{
- numberValue = value;
+ numberValue = BoxedPrimitives.Get(value);
}
else
{
@@ -2152,7 +2152,7 @@ namespace Newtonsoft.Json
if (singleDigit)
{
// digit char values start at 48
- numberValue = (long)firstChar - 48;
+ numberValue = BoxedPrimitives.Get((long)firstChar - 48);
numberType = JsonToken.Integer;
}
else if (nonBase10)
@@ -2161,7 +2161,7 @@ namespace Newtonsoft.Json
try
{
- numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
+ numberValue = BoxedPrimitives.Get(number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8));
}
catch (Exception ex)
{
@@ -2175,7 +2175,7 @@ namespace Newtonsoft.Json
ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out long value);
if (parseResult == ParseResult.Success)
{
- numberValue = value;
+ numberValue = BoxedPrimitives.Get(value);
numberType = JsonToken.Integer;
}
else if (parseResult == ParseResult.Overflow)
@@ -2201,7 +2201,7 @@ namespace Newtonsoft.Json
parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal d);
if (parseResult == ParseResult.Success)
{
- numberValue = d;
+ numberValue = BoxedPrimitives.Get(d);
}
else
{
@@ -2214,7 +2214,7 @@ namespace Newtonsoft.Json
if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double d))
{
- numberValue = d;
+ numberValue = BoxedPrimitives.Get(d);
}
else
{
@@ -2455,7 +2455,7 @@ namespace Newtonsoft.Json
// or the text ends
if (MatchValueWithTrailingSeparator(JsonConvert.True))
{
- SetToken(JsonToken.Boolean, true);
+ SetToken(JsonToken.Boolean, BoxedPrimitives.BooleanTrue);
}
else
{
@@ -2491,7 +2491,7 @@ namespace Newtonsoft.Json
{
if (MatchValueWithTrailingSeparator(JsonConvert.False))
{
- SetToken(JsonToken.Boolean, false);
+ SetToken(JsonToken.Boolean, BoxedPrimitives.BooleanFalse);
}
else
{
@@ -2514,7 +2514,7 @@ namespace Newtonsoft.Json
case ReadType.ReadAsDouble:
if (_floatParseHandling == FloatParseHandling.Double)
{
- SetToken(JsonToken.Float, double.NegativeInfinity);
+ SetToken(JsonToken.Float, BoxedPrimitives.DoubleNegativeInfinity);
return double.NegativeInfinity;
}
break;
@@ -2543,7 +2543,7 @@ namespace Newtonsoft.Json
case ReadType.ReadAsDouble:
if (_floatParseHandling == FloatParseHandling.Double)
{
- SetToken(JsonToken.Float, double.PositiveInfinity);
+ SetToken(JsonToken.Float, BoxedPrimitives.DoublePositiveInfinity);
return double.PositiveInfinity;
}
break;
@@ -2573,7 +2573,7 @@ namespace Newtonsoft.Json
case ReadType.ReadAsDouble:
if (_floatParseHandling == FloatParseHandling.Double)
{
- SetToken(JsonToken.Float, double.NaN);
+ SetToken(JsonToken.Float, BoxedPrimitives.DoubleNaN);
return double.NaN;
}
break;
diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs
index 9e281c60..344ae834 100644
--- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs
+++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs
@@ -187,12 +187,12 @@ namespace Newtonsoft.Json.Linq
base.WritePropertyName(name);
}
- private void AddValue(object? value, JsonToken token)
+ private void AddRawValue(object? value, JTokenType type, JsonToken token)
{
- AddValue(new JValue(value), token);
+ AddJValue(new JValue(value, type), token);
}
- internal void AddValue(JValue? value, JsonToken token)
+ internal void AddJValue(JValue? value, JsonToken token)
{
if (_parent != null)
{
@@ -228,7 +228,7 @@ namespace Newtonsoft.Json.Linq
if (value is BigInteger)
{
InternalWriteValue(JsonToken.Integer);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
else
#endif
@@ -243,7 +243,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteNull()
{
base.WriteNull();
- AddValue(null, JsonToken.Null);
+ AddJValue(JValue.CreateNull(), JsonToken.Null);
}
///
@@ -252,7 +252,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteUndefined()
{
base.WriteUndefined();
- AddValue(null, JsonToken.Undefined);
+ AddJValue(JValue.CreateUndefined(), JsonToken.Undefined);
}
///
@@ -262,7 +262,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteRaw(string? json)
{
base.WriteRaw(json);
- AddValue(new JRaw(json), JsonToken.Raw);
+ AddJValue(new JRaw(json), JsonToken.Raw);
}
///
@@ -272,7 +272,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteComment(string? text)
{
base.WriteComment(text);
- AddValue(JValue.CreateComment(text), JsonToken.Comment);
+ AddJValue(JValue.CreateComment(text), JsonToken.Comment);
}
///
@@ -282,7 +282,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(string? value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.String);
+ AddJValue(new JValue(value), JsonToken.String);
}
///
@@ -292,7 +292,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(int value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -303,7 +303,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(uint value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -313,7 +313,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(long value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddJValue(new JValue(value), JsonToken.Integer);
}
///
@@ -324,7 +324,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(ulong value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddJValue(new JValue(value), JsonToken.Integer);
}
///
@@ -334,7 +334,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(float value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Float);
+ AddJValue(new JValue(value), JsonToken.Float);
}
///
@@ -344,7 +344,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(double value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Float);
+ AddJValue(new JValue(value), JsonToken.Float);
}
///
@@ -354,7 +354,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(bool value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Boolean);
+ AddJValue(new JValue(value), JsonToken.Boolean);
}
///
@@ -364,7 +364,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(short value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -375,7 +375,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(ushort value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -391,7 +391,7 @@ namespace Newtonsoft.Json.Linq
#else
s = value.ToString();
#endif
- AddValue(s, JsonToken.String);
+ AddJValue(new JValue(s), JsonToken.String);
}
///
@@ -401,7 +401,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(byte value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -412,7 +412,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(sbyte value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Integer);
+ AddRawValue(value, JTokenType.Integer, JsonToken.Integer);
}
///
@@ -422,7 +422,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(decimal value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Float);
+ AddJValue(new JValue(value), JsonToken.Float);
}
///
@@ -433,7 +433,7 @@ namespace Newtonsoft.Json.Linq
{
base.WriteValue(value);
value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling);
- AddValue(value, JsonToken.Date);
+ AddJValue(new JValue(value), JsonToken.Date);
}
#if HAVE_DATE_TIME_OFFSET
@@ -444,7 +444,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(DateTimeOffset value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Date);
+ AddJValue(new JValue(value), JsonToken.Date);
}
#endif
@@ -455,7 +455,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(byte[]? value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.Bytes);
+ AddJValue(new JValue(value, JTokenType.Bytes), JsonToken.Bytes);
}
///
@@ -465,7 +465,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(TimeSpan value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.String);
+ AddJValue(new JValue(value), JsonToken.String);
}
///
@@ -475,7 +475,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(Guid value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.String);
+ AddJValue(new JValue(value), JsonToken.String);
}
///
@@ -485,7 +485,7 @@ namespace Newtonsoft.Json.Linq
public override void WriteValue(Uri? value)
{
base.WriteValue(value);
- AddValue(value, JsonToken.String);
+ AddJValue(new JValue(value), JsonToken.String);
}
#endregion
diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs
index 27d5de88..86ea4dfe 100644
--- a/Src/Newtonsoft.Json/Linq/JValue.cs
+++ b/Src/Newtonsoft.Json/Linq/JValue.cs
@@ -72,7 +72,7 @@ namespace Newtonsoft.Json.Linq
///
/// The value.
public JValue(long value)
- : this(value, JTokenType.Integer)
+ : this(BoxedPrimitives.Get(value), JTokenType.Integer)
{
}
@@ -81,7 +81,7 @@ namespace Newtonsoft.Json.Linq
///
/// The value.
public JValue(decimal value)
- : this(value, JTokenType.Float)
+ : this(BoxedPrimitives.Get(value), JTokenType.Float)
{
}
@@ -109,7 +109,7 @@ namespace Newtonsoft.Json.Linq
///
/// The value.
public JValue(double value)
- : this(value, JTokenType.Float)
+ : this(BoxedPrimitives.Get(value), JTokenType.Float)
{
}
@@ -147,7 +147,7 @@ namespace Newtonsoft.Json.Linq
///
/// The value.
public JValue(bool value)
- : this(value, JTokenType.Boolean)
+ : this(BoxedPrimitives.Get(value), JTokenType.Boolean)
{
}
diff --git a/Src/Newtonsoft.Json/Utilities/BoxedPrimitives.cs b/Src/Newtonsoft.Json/Utilities/BoxedPrimitives.cs
new file mode 100644
index 00000000..e6fb4f2d
--- /dev/null
+++ b/Src/Newtonsoft.Json/Utilities/BoxedPrimitives.cs
@@ -0,0 +1,116 @@
+#region License
+// Copyright (c) 2022 James Newton-King
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+#endregion
+
+namespace Newtonsoft.Json.Utilities
+{
+ internal static class BoxedPrimitives
+ {
+ internal static object Get(bool value) => value ? BooleanTrue : BooleanFalse;
+
+ internal static readonly object BooleanTrue = true;
+ internal static readonly object BooleanFalse = false;
+
+ internal static object Get(int value) => value switch
+ {
+ -1 => Int32_M1,
+ 0 => Int32_0,
+ 1 => Int32_1,
+ 2 => Int32_2,
+ 3 => Int32_3,
+ 4 => Int32_4,
+ 5 => Int32_5,
+ 6 => Int32_6,
+ 7 => Int32_7,
+ 8 => Int32_8,
+ _ => value,
+ };
+
+ // integers tend to be weighted towards a handful of low numbers; we could argue
+ // for days over the "correct" range to have special handling, but I'm arbitrarily
+ // mirroring the same decision as the IL opcodes, which has M1 thru 8
+ internal static readonly object Int32_M1 = -1;
+ internal static readonly object Int32_0 = 0;
+ internal static readonly object Int32_1 = 1;
+ internal static readonly object Int32_2 = 2;
+ internal static readonly object Int32_3 = 3;
+ internal static readonly object Int32_4 = 4;
+ internal static readonly object Int32_5 = 5;
+ internal static readonly object Int32_6 = 6;
+ internal static readonly object Int32_7 = 7;
+ internal static readonly object Int32_8 = 8;
+
+ internal static object Get(long value) => value switch
+ {
+ -1 => Int64_M1,
+ 0 => Int64_0,
+ 1 => Int64_1,
+ 2 => Int64_2,
+ 3 => Int64_3,
+ 4 => Int64_4,
+ 5 => Int64_5,
+ 6 => Int64_6,
+ 7 => Int64_7,
+ 8 => Int64_8,
+ _ => value,
+ };
+
+ internal static readonly object Int64_M1 = -1L;
+ internal static readonly object Int64_0 = 0L;
+ internal static readonly object Int64_1 = 1L;
+ internal static readonly object Int64_2 = 2L;
+ internal static readonly object Int64_3 = 3L;
+ internal static readonly object Int64_4 = 4L;
+ internal static readonly object Int64_5 = 5L;
+ internal static readonly object Int64_6 = 6L;
+ internal static readonly object Int64_7 = 7L;
+ internal static readonly object Int64_8 = 8L;
+
+ internal static object Get(decimal value) => value == decimal.Zero ? DecimalZero : value;
+
+ private static readonly object DecimalZero = decimal.Zero;
+
+ internal static object Get(double value)
+ {
+ if (value == 0.0d)
+ {
+ return DoubleZero;
+ }
+ if (double.IsInfinity(value))
+ {
+ return double.IsPositiveInfinity(value) ? DoublePositiveInfinity : DoubleNegativeInfinity;
+ }
+ if (double.IsNaN(value))
+ {
+ return DoubleNaN;
+ }
+ return value;
+ }
+
+ internal static readonly object DoubleNaN = double.NaN;
+ internal static readonly object DoublePositiveInfinity = double.PositiveInfinity;
+ internal static readonly object DoubleNegativeInfinity = double.NegativeInfinity;
+ internal static readonly object DoubleZero = (double)0;
+ }
+}