diff --git a/Jint/Native/Date/DatePrototype.cs b/Jint/Native/Date/DatePrototype.cs index 056f4c6755..c7797ede2a 100644 --- a/Jint/Native/Date/DatePrototype.cs +++ b/Jint/Native/Date/DatePrototype.cs @@ -968,7 +968,7 @@ private JsValue ToJson(JsValue thisObject, JsCallArguments arguments) { var o = TypeConverter.ToObject(_realm, thisObject); var tv = TypeConverter.ToPrimitive(o, Types.Number); - if (tv.IsNumber() && !IsFinite(((JsNumber) tv)._value)) + if (tv.IsNumber() && !double.IsFinite(((JsNumber) tv)._value)) { return Null; } @@ -1420,19 +1420,16 @@ internal static DatePresentation MakeDate(double day, double time) return new DatePresentation((long) (day * MsPerDay + time), DateFlags.None); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsFinite(double value) => !double.IsNaN(value) && !double.IsInfinity(value); - - private static bool AreFinite(double value1, double value2) => IsFinite(value1) && IsFinite(value2); + private static bool AreFinite(double value1, double value2) => double.IsFinite(value1) && double.IsFinite(value2); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsFinite(DatePresentation value) => value.IsFinite; private static bool AreFinite(double value1, double value2, double value3) - => IsFinite(value1) && IsFinite(value2) && IsFinite(value3); + => double.IsFinite(value1) && double.IsFinite(value2) && double.IsFinite(value3); private static bool AreFinite(double value1, double value2, double value3, double value4) - => IsFinite(value1) && IsFinite(value2) && IsFinite(value3) && IsFinite(value4); + => double.IsFinite(value1) && double.IsFinite(value2) && double.IsFinite(value3) && double.IsFinite(value4); [StructLayout(LayoutKind.Auto)] private readonly record struct Date(int Year, int Month, int Day); diff --git a/Jint/Native/Global/GlobalObject.cs b/Jint/Native/Global/GlobalObject.cs index a2609738af..78f6be5038 100644 --- a/Jint/Native/Global/GlobalObject.cs +++ b/Jint/Native/Global/GlobalObject.cs @@ -262,7 +262,7 @@ private static JsValue IsNaN(JsValue thisObject, JsValue value) private static JsValue IsFinite(JsValue thisObject, JsValue value) { var n = TypeConverter.ToNumber(value); - return !double.IsNaN(n) && !double.IsInfinity(n); + return double.IsFinite(n); } private const string UriReservedString = ";/?:@&=+$,"; diff --git a/Jint/Native/Intl/DurationFormatPrototype.cs b/Jint/Native/Intl/DurationFormatPrototype.cs index a712595107..8d3b5f1ae8 100644 --- a/Jint/Native/Intl/DurationFormatPrototype.cs +++ b/Jint/Native/Intl/DurationFormatPrototype.cs @@ -281,7 +281,7 @@ private double GetDurationComponent(ObjectInstance obj, string property) } var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"Invalid value for {property}"); } diff --git a/Jint/Native/Intl/JsNumberFormat.cs b/Jint/Native/Intl/JsNumberFormat.cs index 78d365ed06..6777049872 100644 --- a/Jint/Native/Intl/JsNumberFormat.cs +++ b/Jint/Native/Intl/JsNumberFormat.cs @@ -208,7 +208,7 @@ private string FormatWithNotation(double value) private string FormatScientific(double value) { - if (double.IsNaN(value) || double.IsInfinity(value) || value == 0) + if (!double.IsFinite(value) || value == 0) { return FormatDecimal(value); } @@ -232,7 +232,7 @@ private string FormatScientific(double value) private string FormatEngineering(double value) { - if (double.IsNaN(value) || double.IsInfinity(value) || value == 0) + if (!double.IsFinite(value) || value == 0) { return FormatDecimal(value); } @@ -702,7 +702,7 @@ private void AddDecimalParts(List parts, double value) /// private double ApplyRounding(double value, int decimalPlaces) { - if (double.IsNaN(value) || double.IsInfinity(value)) + if (!double.IsFinite(value)) { return value; } @@ -1214,7 +1214,7 @@ private string FormatDecimal(double value) private string FormatWithSignificantDigits(double value) { - if (double.IsNaN(value) || double.IsInfinity(value)) + if (!double.IsFinite(value)) { return value.ToString(NumberFormatInfo); } diff --git a/Jint/Native/Intl/JsPluralRules.cs b/Jint/Native/Intl/JsPluralRules.cs index 0388120d4a..34b75773b8 100644 --- a/Jint/Native/Intl/JsPluralRules.cs +++ b/Jint/Native/Intl/JsPluralRules.cs @@ -135,7 +135,7 @@ internal string Select(double n) private string SelectCardinal(double n) { // Handle special values - if (double.IsNaN(n) || double.IsInfinity(n)) + if (!double.IsFinite(n)) { return "other"; } @@ -194,7 +194,7 @@ private string SelectCardinal(double n) /// private string SelectOrdinal(double n) { - if (double.IsNaN(n) || double.IsInfinity(n)) + if (!double.IsFinite(n)) { return "other"; } diff --git a/Jint/Native/Intl/NumberFormatConstructor.cs b/Jint/Native/Intl/NumberFormatConstructor.cs index d6c5e49628..333aeed0f3 100644 --- a/Jint/Native/Intl/NumberFormatConstructor.cs +++ b/Jint/Native/Intl/NumberFormatConstructor.cs @@ -668,7 +668,7 @@ private int GetRoundingIncrementOption(ObjectInstance options) } var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, "roundingIncrement must be a finite number"); } diff --git a/Jint/Native/Intl/RelativeTimeFormatPrototype.cs b/Jint/Native/Intl/RelativeTimeFormatPrototype.cs index d8e4ffd464..cc0d2ccec1 100644 --- a/Jint/Native/Intl/RelativeTimeFormatPrototype.cs +++ b/Jint/Native/Intl/RelativeTimeFormatPrototype.cs @@ -53,7 +53,7 @@ private JsValue Format(JsValue thisObject, JsValue value, JsValue unit) var relativeTimeFormat = ValidateRelativeTimeFormat(thisObject); var numericValue = TypeConverter.ToNumber(value); - if (double.IsNaN(numericValue) || double.IsInfinity(numericValue)) + if (!double.IsFinite(numericValue)) { Throw.RangeError(_realm, "Invalid value"); } @@ -77,7 +77,7 @@ private JsArray FormatToParts(JsValue thisObject, JsValue value, JsValue unit) var relativeTimeFormat = ValidateRelativeTimeFormat(thisObject); var numericValue = TypeConverter.ToNumber(value); - if (double.IsNaN(numericValue) || double.IsInfinity(numericValue)) + if (!double.IsFinite(numericValue)) { Throw.RangeError(_realm, "Invalid value"); } diff --git a/Jint/Native/Json/JsonSerializer.cs b/Jint/Native/Json/JsonSerializer.cs index d335667978..de2d6279e6 100644 --- a/Jint/Native/Json/JsonSerializer.cs +++ b/Jint/Native/Json/JsonSerializer.cs @@ -401,7 +401,7 @@ private SerializeResult SerializeJSONValue(JsValue value, ref ValueStringBuilder return SerializeResult.NotUndefined; } - var isFinite = !double.IsNaN(doubleValue) && !double.IsInfinity(doubleValue); + var isFinite = double.IsFinite(doubleValue); if (isFinite) { if (TypeConverter.CanBeStringifiedAsLong(doubleValue)) diff --git a/Jint/Native/Temporal/Duration/DurationPrototype.cs b/Jint/Native/Temporal/Duration/DurationPrototype.cs index de9a2fad94..20232e34d4 100644 --- a/Jint/Native/Temporal/Duration/DurationPrototype.cs +++ b/Jint/Native/Temporal/Duration/DurationPrototype.cs @@ -122,7 +122,7 @@ private double GetOptionalDurationProperty(ObjectInstance obj, string name, doub anyDefined = true; var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"Duration {name} must be a finite number"); } @@ -278,7 +278,7 @@ private JsDuration Round(JsValue thisObject, JsValue options) if (!roundingIncrementValue.IsUndefined()) { roundingIncrement = TypeConverter.ToNumber(roundingIncrementValue); - if (double.IsNaN(roundingIncrement) || double.IsInfinity(roundingIncrement)) + if (!double.IsFinite(roundingIncrement)) { Throw.RangeError(_realm, "roundingIncrement must be a finite number"); } @@ -1381,7 +1381,7 @@ private JsString ToStringMethod(JsValue thisObject, JsValue options) if (fsdValue.IsNumber()) { var fsdNum = fsdValue.AsNumber(); - if (double.IsNaN(fsdNum) || double.IsInfinity(fsdNum)) + if (!double.IsFinite(fsdNum)) { Throw.RangeError(_realm, "fractionalSecondDigits must be 'auto' or a number 0-9"); } diff --git a/Jint/Native/Temporal/Instant/InstantConstructor.cs b/Jint/Native/Temporal/Instant/InstantConstructor.cs index e5aaff8320..7ca4d03f89 100644 --- a/Jint/Native/Temporal/Instant/InstantConstructor.cs +++ b/Jint/Native/Temporal/Instant/InstantConstructor.cs @@ -55,7 +55,7 @@ private JsInstant FromEpochMilliseconds(JsValue thisObject, JsValue epochMillise var ms = TypeConverter.ToNumber(epochMilliseconds); // NumberToBigInt: must be an integral number - if (double.IsNaN(ms) || double.IsInfinity(ms)) + if (!double.IsFinite(ms)) { Throw.RangeError(_realm, "Invalid epoch milliseconds"); } diff --git a/Jint/Native/Temporal/Instant/InstantPrototype.cs b/Jint/Native/Temporal/Instant/InstantPrototype.cs index 331e737e46..0c1a3b65b8 100644 --- a/Jint/Native/Temporal/Instant/InstantPrototype.cs +++ b/Jint/Native/Temporal/Instant/InstantPrototype.cs @@ -190,7 +190,7 @@ private JsDuration DifferenceTemporalInstant(JsValue thisObject, JsCallArguments if (!incValue.IsUndefined()) { var inc = TypeConverter.ToNumber(incValue); - if (double.IsNaN(inc) || double.IsInfinity(inc)) + if (!double.IsFinite(inc)) { Throw.RangeError(_realm, "roundingIncrement must be finite"); } @@ -323,7 +323,7 @@ private JsInstant Round(JsValue thisObject, JsValue roundTo) if (!roundingIncrementValue.IsUndefined()) { var inc = TypeConverter.ToNumber(roundingIncrementValue); - if (double.IsNaN(inc) || double.IsInfinity(inc)) + if (!double.IsFinite(inc)) { Throw.RangeError(_realm, "roundingIncrement must be finite"); } @@ -561,7 +561,7 @@ private JsString ToStringMethod(JsValue thisObject, JsValue options) else { var num = ((JsNumber) digitsValue)._value; - if (double.IsNaN(num) || double.IsInfinity(num)) + if (!double.IsFinite(num)) { Throw.RangeError(_realm, "fractionalSecondDigits must be finite"); } diff --git a/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs b/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs index aa7288399b..16c599ba09 100644 --- a/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs +++ b/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs @@ -799,7 +799,7 @@ private JsString ToString(JsValue thisObject, JsValue options) if (fsdValue.IsNumber()) { var fsdNum = fsdValue.AsNumber(); - if (double.IsNaN(fsdNum) || double.IsInfinity(fsdNum)) + if (!double.IsFinite(fsdNum)) { Throw.RangeError(_realm, "fractionalSecondDigits must be a finite number"); } @@ -1141,7 +1141,7 @@ private static string FormatDateTime(IsoDateTime dateTime, string calendar, stri private int ConvertToInteger(JsValue value, string fieldName) { var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"DateTime {fieldName} must be a finite number"); } @@ -1239,7 +1239,7 @@ private double GetDurationProperty(ObjectInstance obj, string name, ref bool has hasAny = true; var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"Duration {name} must be a finite number"); } diff --git a/Jint/Native/Temporal/PlainTime/PlainTimeConstructor.cs b/Jint/Native/Temporal/PlainTime/PlainTimeConstructor.cs index 343d573c4b..669d6af59c 100644 --- a/Jint/Native/Temporal/PlainTime/PlainTimeConstructor.cs +++ b/Jint/Native/Temporal/PlainTime/PlainTimeConstructor.cs @@ -257,7 +257,7 @@ private int GetTimeProperty(ObjectInstance obj, string name, int defaultValue, r hasAny = true; var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"Time {name} must be a finite number"); } diff --git a/Jint/Native/Temporal/PlainTime/PlainTimePrototype.cs b/Jint/Native/Temporal/PlainTime/PlainTimePrototype.cs index 5194859c1c..82b061d5e9 100644 --- a/Jint/Native/Temporal/PlainTime/PlainTimePrototype.cs +++ b/Jint/Native/Temporal/PlainTime/PlainTimePrototype.cs @@ -409,7 +409,7 @@ private JsString ToString(JsValue thisObject, JsValue options) if (fsdValue.IsNumber()) { var fsdNum = fsdValue.AsNumber(); - if (double.IsNaN(fsdNum) || double.IsInfinity(fsdNum)) + if (!double.IsFinite(fsdNum)) { Throw.RangeError(_realm, "fractionalSecondDigits must be a finite number"); } diff --git a/Jint/Native/Temporal/TemporalHelpers.cs b/Jint/Native/Temporal/TemporalHelpers.cs index a136d549b2..daf9ca360b 100644 --- a/Jint/Native/Temporal/TemporalHelpers.cs +++ b/Jint/Native/Temporal/TemporalHelpers.cs @@ -1134,7 +1134,7 @@ public static bool IsValidDuration(DurationRecord duration) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool CheckDurationComponent(double component, ref int sign) { - if (double.IsNaN(component) || double.IsInfinity(component)) + if (!double.IsFinite(component)) return false; if (component > 0) @@ -4944,7 +4944,7 @@ public static int GetRoundingIncrementOption(Realm realm, JsValue normalizedOpti var number = TypeConverter.ToNumber(increment); // 6. If integerIncrement is NaN, +∞, or -∞, throw a RangeError exception. - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(realm, "Rounding increment must be finite"); } diff --git a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs index 68f59d5ce9..55e6cf4071 100644 --- a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs +++ b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs @@ -982,7 +982,7 @@ private BigInteger ToBigInt(JsValue value) } var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, "epochNanoseconds must be a finite number"); } diff --git a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs index 5b1a48ab25..ca6dde0c0a 100644 --- a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs +++ b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs @@ -965,7 +965,7 @@ private JsString ToStringMethod(JsValue thisObject, JsValue options) else { var num = ((JsNumber) precisionProp)._value; - if (double.IsNaN(num) || double.IsInfinity(num)) + if (!double.IsFinite(num)) { Throw.RangeError(_realm, "fractionalSecondDigits must be finite"); } @@ -1398,7 +1398,7 @@ private double GetDoubleProperty(ObjectInstance obj, string name, double default hasAny = true; var number = TypeConverter.ToNumber(value); - if (double.IsNaN(number) || double.IsInfinity(number)) + if (!double.IsFinite(number)) { Throw.RangeError(_realm, $"Duration {name} must be a finite number"); } diff --git a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs index 4e1d8e5bbe..f5c7c43ad5 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs @@ -227,7 +227,9 @@ protected static JsValue Remainder(EvaluationContext context, JsValue left, JsVa var n = left.AsNumber(); var d = right.AsNumber(); - if (double.IsNaN(n) || double.IsNaN(d) || double.IsInfinity(n)) + // A non-finite dividend, or a NaN divisor, gives NaN. An infinite *divisor* does not, + // and is handled by the branch below, so only n gets the full finiteness test. + if (!double.IsFinite(n) || double.IsNaN(d)) { result = JsNumber.DoubleNaN; } diff --git a/Jint/Runtime/TypeConverter.cs b/Jint/Runtime/TypeConverter.cs index 880d2e401a..e0523d5641 100644 --- a/Jint/Runtime/TypeConverter.cs +++ b/Jint/Runtime/TypeConverter.cs @@ -1022,12 +1022,12 @@ public static ObjectInstance ToObject(Realm realm, JsValue value) internal static bool IsIntegralNumber(double value) { // Math.Floor(value) == value instead of value % 1 == 0: the remainder operator on doubles - // compiles to a native fmod call, Math.Floor is a JIT intrinsic (a single vroundsd). The NaN - // and infinity guards above still short-circuit, and for every finite value the two tests are + // compiles to a native fmod call, Math.Floor is a JIT intrinsic (a single vroundsd). The + // finiteness guard still short-circuits, and for every finite value the two tests are // equivalent - value % 1 is value - trunc(value), which is +-0 exactly when value has no // fractional part, which is exactly when Math.Floor(value) == value (-0.0 and magnitudes // >= 2^52, which are necessarily integral, are accepted by both). - return !double.IsNaN(value) && !double.IsInfinity(value) && Math.Floor(value) == value; + return double.IsFinite(value) && Math.Floor(value) == value; } private static ObjectInstance ToObjectNonObject(Realm realm, JsValue value)