From 43c5d5c05342703f2db326ccaddc052806bb3072 Mon Sep 17 00:00:00 2001 From: Valerie R Young Date: Tue, 13 Nov 2018 13:00:20 -0500 Subject: [PATCH] Correct infinity comparisons and toNumber --- src/util/cast.js | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/util/cast.js b/src/util/cast.js index 604100ec8..6cd2162cb 100644 --- a/src/util/cast.js +++ b/src/util/cast.js @@ -30,6 +30,7 @@ class Cast { static toNumber (value) { // If value is already a number we don't need to coerce it with // Number(). + if (typeof value === 'number') { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -38,8 +39,14 @@ class Cast { } return value; } - - const n = Number(value); + let n; + if (Cast.isInfinity(value)) { + n = Infinity; + } else if (Cast.isNegativeInfinity(value)) { + n = -Infinity; + } else { + n = Number(value); + } if (_NumberIsNaN(n)) { // Scratch treats NaN as 0, when needed as a number. // E.g., 0 + NaN -> 0. @@ -117,6 +124,24 @@ class Cast { return val === null || (typeof val === 'string' && val.trim().length === 0); } + /** + * Determine if a Scratch argument is infinity. + * @param {*} val value to check. + * @return {boolean} True if the argument is any capitalization of infinity. + */ + static isInfinity (val) { + return val === Infinity || (typeof val === 'string' && val.toLowerCase() === 'infinity'); + } + + /** + * Determine if a Scratch argument is negative infinity. + * @param {*} val value to check. + * @return {boolean} True if the argument is a '-' folled by any capitalization of infinity. + */ + static isNegativeInfinity (val) { + return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity'); + } + /** * Compare two values, using Scratch cast, case-insensitive string compare, etc. * In Scratch 2.0, this is captured by `interp.compare.` @@ -125,8 +150,10 @@ class Cast { * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise. */ static compare (v1, v2) { - let n1 = Number(v1); - let n2 = Number(v2); + // If Cast.toNumber returns '0', then the value might be NaN. If the value is NaN, + // this comparison algorithm needs to know. + let n1 = Cast.toNumber(v1) || Number(v1); + let n2 = Cast.toNumber(v2) || Number(v2); if (n1 === 0 && Cast.isWhiteSpace(v1)) { n1 = NaN; } else if (n2 === 0 && Cast.isWhiteSpace(v2)) { @@ -145,8 +172,16 @@ class Cast { return 0; } // Compare as numbers. - return n1 - n2; - + const r = n1 - n2; + if (isNaN(r)) { + if (n1 === Infinity && n2 === Infinity) { + return 0; + } + if (n1 === -Infinity && n2 === -Infinity) { + return 0; + } + } + return r; } /**