Skip to content

Commit 222f4f5

Browse files
committed
Require explicit array usage on certain operators
1 parent fc79246 commit 222f4f5

6 files changed

+138
-188
lines changed

build.test.js

-61
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { LogicEngine, AsyncLogicEngine } from './index.js'
2-
import InvalidControlInput from './errors/InvalidControlInput.js'
32

43
function timeout (n, x) {
54
return new Promise((resolve) => {
@@ -135,66 +134,6 @@ function timeout (n, x) {
135134
expect(await f()).toEqual(1 / 2)
136135
})
137136

138-
test('or operator w/ variable input', async () => {
139-
const f = await logic.build({ or: { preserve: [true, false] } })
140-
141-
expect(await f()).toEqual(true)
142-
})
143-
144-
test('and operator w/ variable input', async () => {
145-
const f = await logic.build({ and: { preserve: [true, false] } })
146-
147-
expect(await f()).toEqual(false)
148-
})
149-
150-
test('<= operator w/ variable input', async () => {
151-
const f = await logic.build({ '<=': { preserve: [1, 2] } })
152-
153-
expect(await f()).toEqual(true)
154-
})
155-
156-
test('< operator w/ variable input', async () => {
157-
const f = await logic.build({ '<': { preserve: [1, 2] } })
158-
159-
expect(await f()).toEqual(true)
160-
})
161-
162-
test('>= operator w/ variable input', async () => {
163-
const f = await logic.build({ '>=': { preserve: [1, 2] } })
164-
165-
expect(await f()).toEqual(false)
166-
})
167-
168-
test('> operator w/ variable input', async () => {
169-
const f = await logic.build({ '>': { preserve: [1, 2] } })
170-
171-
expect(await f()).toEqual(false)
172-
})
173-
174-
test('== operator w/ variable input', async () => {
175-
const f = await logic.build({ '==': { preserve: [1, 2] } })
176-
177-
expect(await f()).toEqual(false)
178-
})
179-
180-
test('=== operator w/ variable input', async () => {
181-
const f = await logic.build({ '===': { preserve: [1, 2] } })
182-
183-
expect(await f()).toEqual(false)
184-
})
185-
186-
test('!== operator w/ variable input', async () => {
187-
const f = await logic.build({ '!==': { preserve: [1, 2] } })
188-
189-
expect(await f()).toEqual(true)
190-
})
191-
192-
test('!= operator w/ variable input', async () => {
193-
const f = await logic.build({ '!=': { preserve: [1, 2] } })
194-
195-
expect(await f()).toEqual(true)
196-
})
197-
198137
test('min command w/ variable input', async () => {
199138
const f = await logic.build({ min: { preserve: [1, 2] } })
200139

defaultMethods.js

+34-66
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Sync, isSync, Unfound, OriginalImpl, Compiled } from './constants.js'
66
import declareSync from './utilities/declareSync.js'
77
import { build, buildString } from './compiler.js'
88
import chainingSupported from './utilities/chainingSupported.js'
9-
import InvalidControlInput from './errors/InvalidControlInput.js'
109
import legacyMethods from './legacy.js'
1110
import { precoerceNumber } from './utilities/downgrade.js'
1211

@@ -158,7 +157,7 @@ const defaultMethods = {
158157
},
159158
if: {
160159
method: (input, context, above, engine) => {
161-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
160+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
162161

163162
if (input.length === 1) return runOptimizedOrFallback(input[0], engine, context, above)
164163
if (input.length < 2) return null
@@ -187,7 +186,7 @@ const defaultMethods = {
187186
return isDeterministic(data, buildState.engine, buildState)
188187
},
189188
asyncMethod: async (input, context, above, engine) => {
190-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
189+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
191190

192191
// check the bounds
193192
if (input.length === 1) return engine.run(input[0], context, { above })
@@ -225,31 +224,25 @@ const defaultMethods = {
225224
// eslint-disable-next-line eqeqeq
226225
'!=': createComparator('!=', (a, b) => a != b),
227226
'!==': createComparator('!==', (a, b) => a !== b),
228-
// Why "executeInLoop"? Because if it needs to execute to get an array, I do not want to execute the arguments,
229-
// Both for performance and safety reasons.
230227
or: {
231228
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
232229
method: (arr, context, above, engine) => {
233-
// See "executeInLoop" above
234-
const executeInLoop = Array.isArray(arr)
235-
if (!executeInLoop) arr = runOptimizedOrFallback(arr, engine, context, above)
230+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
236231

237232
let item
238233
for (let i = 0; i < arr.length; i++) {
239-
item = executeInLoop ? runOptimizedOrFallback(arr[i], engine, context, above) : arr[i]
234+
item = runOptimizedOrFallback(arr[i], engine, context, above)
240235
if (engine.truthy(item)) return item
241236
}
242237

243238
return item
244239
},
245240
asyncMethod: async (arr, _1, _2, engine) => {
246-
// See "executeInLoop" above
247-
const executeInLoop = Array.isArray(arr)
248-
if (!executeInLoop) arr = await engine.run(arr, _1, { above: _2 })
241+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
249242

250243
let item
251244
for (let i = 0; i < arr.length; i++) {
252-
item = executeInLoop ? await engine.run(arr[i], _1, { above: _2 }) : arr[i]
245+
item = await engine.run(arr[i], _1, { above: _2 })
253246
if (engine.truthy(item)) return item
254247
}
255248

@@ -270,27 +263,23 @@ const defaultMethods = {
270263
'??': {
271264
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
272265
method: (arr, context, above, engine) => {
273-
// See "executeInLoop" above
274-
const executeInLoop = Array.isArray(arr)
275-
if (!executeInLoop) arr = runOptimizedOrFallback(arr, engine, context, above)
266+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
276267

277268
let item
278269
for (let i = 0; i < arr.length; i++) {
279-
item = executeInLoop ? runOptimizedOrFallback(arr[i], engine, context, above) : arr[i]
270+
item = runOptimizedOrFallback(arr[i], engine, context, above)
280271
if (item !== null && item !== undefined) return item
281272
}
282273

283274
if (item === undefined) return null
284275
return item
285276
},
286277
asyncMethod: async (arr, _1, _2, engine) => {
287-
// See "executeInLoop" above
288-
const executeInLoop = Array.isArray(arr)
289-
if (!executeInLoop) arr = await engine.run(arr, _1, { above: _2 })
278+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
290279

291280
let item
292281
for (let i = 0; i < arr.length; i++) {
293-
item = executeInLoop ? await engine.run(arr[i], _1, { above: _2 }) : arr[i]
282+
item = await engine.run(arr[i], _1, { above: _2 })
294283
if (item !== null && item !== undefined) return item
295284
}
296285

@@ -315,17 +304,15 @@ const defaultMethods = {
315304
try: {
316305
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
317306
method: (arr, context, above, engine) => {
318-
// See "executeInLoop" above
319-
const executeInLoop = Array.isArray(arr)
320-
if (!executeInLoop) arr = runOptimizedOrFallback(arr, engine, context, above)
307+
if (!Array.isArray(arr)) arr = [arr]
321308

322309
let item
323310
let lastError
324311
for (let i = 0; i < arr.length; i++) {
325312
try {
326313
// Todo: make this message thing more robust.
327314
if (lastError) item = runOptimizedOrFallback(arr[i], engine, { type: lastError.type || lastError.error || lastError.message || lastError.constructor.name }, [null, context, above])
328-
else item = executeInLoop ? runOptimizedOrFallback(arr[i], engine, context, above) : arr[i]
315+
else item = runOptimizedOrFallback(arr[i], engine, context, above)
329316
return item
330317
} catch (e) {
331318
if (Number.isNaN(e)) lastError = { message: 'NaN' }
@@ -336,17 +323,15 @@ const defaultMethods = {
336323
throw lastError
337324
},
338325
asyncMethod: async (arr, _1, _2, engine) => {
339-
// See "executeInLoop" above
340-
const executeInLoop = Array.isArray(arr)
341-
if (!executeInLoop) arr = await engine.run(arr, _1, { above: _2 })
326+
if (!Array.isArray(arr)) arr = [arr]
342327

343328
let item
344329
let lastError
345330
for (let i = 0; i < arr.length; i++) {
346331
try {
347332
// Todo: make this message thing more robust.
348333
if (lastError) item = await engine.run(arr[i], { type: lastError.type || lastError.error || lastError.message || lastError.constructor.name }, { above: [null, _1, _2] })
349-
else item = executeInLoop ? await engine.run(arr[i], _1, { above: _2 }) : arr[i]
334+
else item = await engine.run(arr[i], _1, { above: _2 })
350335
return item
351336
} catch (e) {
352337
if (Number.isNaN(e)) lastError = { message: 'NaN' }
@@ -397,25 +382,21 @@ const defaultMethods = {
397382
and: {
398383
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
399384
method: (arr, context, above, engine) => {
400-
// See "executeInLoop" above
401-
const executeInLoop = Array.isArray(arr)
402-
if (!executeInLoop) arr = runOptimizedOrFallback(arr, engine, context, above)
385+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
403386

404387
let item
405388
for (let i = 0; i < arr.length; i++) {
406-
item = executeInLoop ? runOptimizedOrFallback(arr[i], engine, context, above) : arr[i]
389+
item = runOptimizedOrFallback(arr[i], engine, context, above)
407390
if (!engine.truthy(item)) return item
408391
}
409392
return item
410393
},
411394
asyncMethod: async (arr, _1, _2, engine) => {
412-
// See "executeInLoop" above
413-
const executeInLoop = Array.isArray(arr)
414-
if (!executeInLoop) arr = await engine.run(arr, _1, { above: _2 })
395+
if (!Array.isArray(arr)) throw INVALID_ARGUMENTS
415396

416397
let item
417398
for (let i = 0; i < arr.length; i++) {
418-
item = executeInLoop ? await engine.run(arr[i], _1, { above: _2 }) : arr[i]
399+
item = await engine.run(arr[i], _1, { above: _2 })
419400
if (!engine.truthy(item)) return item
420401
}
421402
return item
@@ -539,7 +520,7 @@ const defaultMethods = {
539520
some: {
540521
...createArrayIterativeMethod('some', true),
541522
method: (input, context, above, engine) => {
542-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
523+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
543524
let [selector, mapper] = input
544525

545526
selector = runOptimizedOrFallback(selector, engine, context, above) || []
@@ -553,12 +534,11 @@ const defaultMethods = {
553534
all: {
554535
[Sync]: oldAll[Sync],
555536
method: (args, context, above, engine) => {
556-
if (Array.isArray(args)) {
557-
const first = runOptimizedOrFallback(args[0], engine, context, above)
558-
if (Array.isArray(first) && first.length === 0) return false
559-
}
560-
let [selector, mapper] = args
561-
selector = runOptimizedOrFallback(selector, engine, context, above) || []
537+
if (!Array.isArray(args)) throw INVALID_ARGUMENTS
538+
const selector = runOptimizedOrFallback(args[0], engine, context, above) || []
539+
if (Array.isArray(selector) && selector.length === 0) return false
540+
541+
const mapper = args[1]
562542

563543
for (let i = 0; i < selector.length; i++) {
564544
if (!engine.truthy(runOptimizedOrFallback(mapper, engine, selector[i], [selector, context, above]))) return false
@@ -613,7 +593,7 @@ const defaultMethods = {
613593
)
614594
},
615595
compile: (data, buildState) => {
616-
if (!Array.isArray(data)) throw new InvalidControlInput(data)
596+
if (!Array.isArray(data)) throw INVALID_ARGUMENTS
617597
const { async } = buildState
618598
let [selector, mapper, defaultValue] = data
619599
selector = buildString(selector, buildState)
@@ -652,7 +632,7 @@ const defaultMethods = {
652632
}]({ accumulator: a, current: b }, ${aboveArray}))`
653633
},
654634
method: (input, context, above, engine) => {
655-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
635+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
656636
let [selector, mapper, defaultValue] = input
657637
defaultValue = runOptimizedOrFallback(defaultValue, engine, context, above)
658638
selector = runOptimizedOrFallback(selector, engine, context, above) || []
@@ -669,7 +649,7 @@ const defaultMethods = {
669649
},
670650
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
671651
asyncMethod: async (input, context, above, engine) => {
672-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
652+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
673653
let [selector, mapper, defaultValue] = input
674654
defaultValue = await engine.run(defaultValue, context, {
675655
above
@@ -771,7 +751,7 @@ const defaultMethods = {
771751
return isDeterministic(i, buildState.engine, buildState)
772752
})
773753
}
774-
throw new InvalidControlInput(data)
754+
throw INVALID_ARGUMENTS
775755
},
776756
compile: (data, buildState) => {
777757
// what's nice about this is that I don't have to worry about whether it's async or not, the lower entries take care of that ;)
@@ -788,7 +768,7 @@ const defaultMethods = {
788768
.join(',')} })`
789769
return result
790770
}
791-
throw new InvalidControlInput(data)
771+
throw INVALID_ARGUMENTS
792772
},
793773
asyncMethod: async (object, context, above, engine) => {
794774
const result = await asyncIterators.reduce(
@@ -812,13 +792,7 @@ function createComparator (name, func) {
812792
const opStr = { [Compiled]: name }
813793
return {
814794
method: (args, context, above, engine) => {
815-
if (!Array.isArray(args)) {
816-
const items = runOptimizedOrFallback(args, engine, context, above)
817-
if (items.length === 2) return func(items[0], items[1])
818-
for (let i = 1; i < items.length; i++) {
819-
if (!func(items[i - 1], items[i])) return false
820-
}
821-
}
795+
if (!Array.isArray(args)) throw INVALID_ARGUMENTS
822796
if (args.length === 2) return func(runOptimizedOrFallback(args[0], engine, context, above), runOptimizedOrFallback(args[1], engine, context, above))
823797
let prev = runOptimizedOrFallback(args[0], engine, context, above)
824798
for (let i = 1; i < args.length; i++) {
@@ -829,13 +803,7 @@ function createComparator (name, func) {
829803
return true
830804
},
831805
asyncMethod: async (args, context, above, engine) => {
832-
if (!Array.isArray(args)) {
833-
const items = await runOptimizedOrFallback(args, engine, context, above)
834-
if (items.length === 2) return func(items[0], items[1])
835-
for (let i = 1; i < items.length; i++) {
836-
if (!func(items[i - 1], items[i])) return false
837-
}
838-
}
806+
if (!Array.isArray(args)) throw INVALID_ARGUMENTS
839807
if (args.length === 2) return func(await runOptimizedOrFallback(args[0], engine, context, above), await runOptimizedOrFallback(args[1], engine, context, above))
840808
let prev = await runOptimizedOrFallback(args[0], engine, context, above)
841809
for (let i = 1; i < args.length; i++) {
@@ -873,7 +841,7 @@ function createArrayIterativeMethod (name, useTruthy = false) {
873841
},
874842
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
875843
method: (input, context, above, engine) => {
876-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
844+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
877845
let [selector, mapper] = input
878846

879847
selector = runOptimizedOrFallback(selector, engine, context, above) || []
@@ -885,7 +853,7 @@ function createArrayIterativeMethod (name, useTruthy = false) {
885853
})
886854
},
887855
asyncMethod: async (input, context, above, engine) => {
888-
if (!Array.isArray(input)) throw new InvalidControlInput(input)
856+
if (!Array.isArray(input)) throw INVALID_ARGUMENTS
889857
let [selector, mapper] = input
890858
selector = (await engine.run(selector, context, { above })) || []
891859
return asyncIterators[name](selector, async (i, index) => {
@@ -897,7 +865,7 @@ function createArrayIterativeMethod (name, useTruthy = false) {
897865
})
898866
},
899867
compile: (data, buildState) => {
900-
if (!Array.isArray(data)) throw new InvalidControlInput(data)
868+
if (!Array.isArray(data)) throw INVALID_ARGUMENTS
901869
const { async } = buildState
902870
const [selector, mapper] = data
903871

errors/InvalidControlInput.js

-10
This file was deleted.

0 commit comments

Comments
 (0)