Skip to content

Commit e9ce948

Browse files
committed
Adds NaN checks for comparison operators
1 parent 72876d3 commit e9ce948

File tree

5 files changed

+881
-9
lines changed

5 files changed

+881
-9
lines changed

compiler.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import asyncIterators from './async_iterators.js'
1212
import { coerceArray } from './utilities/coerceArray.js'
1313
import { countArguments } from './utilities/countArguments.js'
14-
import { precoerceNumber, assertSize } from './utilities/downgrade.js'
14+
import { precoerceNumber, assertSize, compareCheck } from './utilities/downgrade.js'
1515

1616
/**
1717
* Provides a simple way to compile logic into a function that can be run.
@@ -315,12 +315,12 @@ function processBuiltString (method, str, buildState) {
315315
str = str.replace(`__%%%${x}%%%__`, item)
316316
})
317317

318-
const final = `(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, precoerceNumber, assertSize) => ${buildState.asyncDetected ? 'async' : ''} (context ${buildState.extraArguments ? ',' + buildState.extraArguments : ''}) => { ${str.includes('prev') ? 'let prev;' : ''} const result = ${str}; return result }`
318+
const final = `(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, precoerceNumber, assertSize, compareCheck) => ${buildState.asyncDetected ? 'async' : ''} (context ${buildState.extraArguments ? ',' + buildState.extraArguments : ''}) => { ${str.includes('prev') ? 'let prev;' : ''} const result = ${str}; return result }`
319319
// console.log(str)
320320
// console.log(final)
321321
// eslint-disable-next-line no-eval
322322
return Object.assign(
323-
(typeof globalThis !== 'undefined' ? globalThis : global).eval(final)(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, precoerceNumber, assertSize), {
323+
(typeof globalThis !== 'undefined' ? globalThis : global).eval(final)(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, precoerceNumber, assertSize, compareCheck), {
324324
[Sync]: !buildState.asyncDetected,
325325
aboveDetected: typeof str === 'string' && str.includes(', above')
326326
})

defaultMethods.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -793,21 +793,45 @@ function createComparator (name, func) {
793793
return {
794794
method: (args, context, above, engine) => {
795795
if (!Array.isArray(args) || args.length <= 1) throw INVALID_ARGUMENTS
796-
if (args.length === 2) return func(runOptimizedOrFallback(args[0], engine, context, above), runOptimizedOrFallback(args[1], engine, context, above))
796+
if (args.length === 2) {
797+
const a = runOptimizedOrFallback(args[0], engine, context, above)
798+
const b = runOptimizedOrFallback(args[1], engine, context, above)
799+
if (typeof a !== typeof b) {
800+
if (typeof a === 'string' && Number.isNaN(+a)) throw NaN
801+
if (typeof b === 'string' && Number.isNaN(+b)) throw NaN
802+
}
803+
return func(a, b)
804+
}
797805
let prev = runOptimizedOrFallback(args[0], engine, context, above)
798806
for (let i = 1; i < args.length; i++) {
799807
const current = runOptimizedOrFallback(args[i], engine, context, above)
808+
if (typeof current !== typeof prev) {
809+
if (typeof current === 'string' && Number.isNaN(+current)) throw NaN
810+
if (i === 1 && typeof prev === 'string' && Number.isNaN(+prev)) throw NaN
811+
}
800812
if (!func(prev, current)) return false
801813
prev = current
802814
}
803815
return true
804816
},
805817
asyncMethod: async (args, context, above, engine) => {
806818
if (!Array.isArray(args) || args.length <= 1) throw INVALID_ARGUMENTS
807-
if (args.length === 2) return func(await runOptimizedOrFallback(args[0], engine, context, above), await runOptimizedOrFallback(args[1], engine, context, above))
819+
if (args.length === 2) {
820+
const a = await runOptimizedOrFallback(args[0], engine, context, above)
821+
const b = await runOptimizedOrFallback(args[1], engine, context, above)
822+
if (typeof a !== typeof b) {
823+
if (typeof a === 'string' && Number.isNaN(+a)) throw NaN
824+
if (typeof b === 'string' && Number.isNaN(+b)) throw NaN
825+
}
826+
return func(a, b)
827+
}
808828
let prev = await runOptimizedOrFallback(args[0], engine, context, above)
809829
for (let i = 1; i < args.length; i++) {
810830
const current = await runOptimizedOrFallback(args[i], engine, context, above)
831+
if (typeof current !== typeof prev) {
832+
if (typeof current === 'string' && Number.isNaN(+current)) throw NaN
833+
if (i === 1 && typeof prev === 'string' && Number.isNaN(+prev)) throw NaN
834+
}
811835
if (!func(prev, current)) return false
812836
prev = current
813837
}
@@ -816,9 +840,9 @@ function createComparator (name, func) {
816840
compile: (data, buildState) => {
817841
if (!Array.isArray(data)) return false
818842
if (data.length < 2) return false
819-
if (data.length === 2) return buildState.compile`(${data[0]} ${opStr} ${data[1]})`
820-
let res = buildState.compile`(${data[0]} ${opStr} (prev = ${data[1]}))`
821-
for (let i = 2; i < data.length; i++) res = buildState.compile`(${res} && prev ${opStr} (prev = ${data[i]}))`
843+
if (data.length === 2) return buildState.compile`((prev = ${data[0]}) ${opStr} compareCheck(${data[1]}, prev))`
844+
let res = buildState.compile`((prev = ${data[0]}) ${opStr} (prev = compareCheck(${data[1]}, prev)))`
845+
for (let i = 2; i < data.length; i++) res = buildState.compile`(${res} && prev ${opStr} (prev = compareCheck(${data[i]}, prev)))`
822846
return res
823847
},
824848
[OriginalImpl]: true,

optimizer.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,16 @@ function checkIdioms (logic, engine, above) {
117117
// Hyper-Optimizations for Comparison Operators.
118118
for (const comparison in comparisons) {
119119
if (logic[comparison] && Array.isArray(logic[comparison]) && engine.methods[comparison][OriginalImpl]) {
120-
const comparisonFunc = comparisons[comparison]
120+
const _comparisonFunc = comparisons[comparison]
121+
122+
function comparisonFunc (a, b) {
123+
if (typeof a !== typeof b) {
124+
if (typeof a === 'string' && Number.isNaN(+a)) throw NaN
125+
if (typeof b === 'string' && Number.isNaN(+b)) throw NaN
126+
}
127+
return _comparisonFunc(a, b)
128+
}
129+
121130
if (logic[comparison].length === 2) {
122131
const [a, b] = logic[comparison]
123132
const A = optimize(a, engine, above)

0 commit comments

Comments
 (0)