From 954cde7062e82c0bd1d69c3409bfd562831bb5d9 Mon Sep 17 00:00:00 2001 From: Kyle Hensel Date: Tue, 20 Oct 2026 00:22:33 +1300 Subject: [PATCH] remove `eval()` --- src/graph/FilterCreator.ts | 100 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/src/graph/FilterCreator.ts b/src/graph/FilterCreator.ts index 994f76652..fb797baf0 100644 --- a/src/graph/FilterCreator.ts +++ b/src/graph/FilterCreator.ts @@ -1,8 +1,7 @@ import { Image } from "./Image"; import { FilterExpression, - FilterOperator, - FilterValue, + FilterKey, } from "./FilterExpression"; import { Func } from "../util/Func"; @@ -42,63 +41,66 @@ export class FilterCreator { * indicates whether the image passed the test or not. */ public createFilter(filter: FilterExpression): FilterFunction { - return new Function("node", "return " + this._compile(filter) + ";"); - } - - private _compile(filter: FilterExpression): string { if (filter == null || filter.length <= 1) { - return "true"; + return () => true; } - const operator: FilterOperator = filter[0]; - const operation: string = - operator === "==" ? this._compileComparisonOp("===", filter[1], filter[2], false) : - operator === "!=" ? this._compileComparisonOp("!==", filter[1], filter[2], false) : - operator === ">" || - operator === ">=" || - operator === "<" || - operator === "<=" ? this._compileComparisonOp(operator, filter[1], filter[2], true) : - operator === "in" ? - this._compileInOp(filter[1], filter.slice(2)) : - operator === "!in" ? - this._compileNegation( - this._compileInOp(filter[1], filter.slice(2))) : - operator === "all" ? this._compileLogicalOp(filter.slice(1), "&&") : - "true"; - - return "(" + operation + ")"; - } - - private _compare(a: T, b: T): number { - return a < b ? -1 : a > b ? 1 : 0; - } - - private _compileComparisonOp(operator: string, property: string, value: T, checkType: boolean): string { - const left: string = this._compilePropertyReference(property); - const right: string = JSON.stringify(value); - - return (checkType ? "typeof " + left + "===typeof " + right + "&&" : "") + left + operator + right; + switch (filter[0]) { + case "==": + return this._compileComparisonOp((a, b) => a === b, filter[1], filter[2], false); + case "!=": + return this._compileComparisonOp((a, b) => a !== b, filter[1], filter[2], false); + case ">": + return this._compileComparisonOp((a, b) => a > b, filter[1], filter[2], true); + case ">=": + return this._compileComparisonOp((a, b) => a >= b, filter[1], filter[2], true); + case "<": + return this._compileComparisonOp((a, b) => a < b, filter[1], filter[2], true); + case "<=": + return this._compileComparisonOp((a, b) => a <= b, filter[1], filter[2], true); + case "in": { + const [, property, ...values] = filter; + return this._compileInOp(property, new Set(values)); + } + case "!in": { + const [, property, ...values] = filter; + return this._compileNegation(this._compileInOp(property, new Set(values))); + } + case "all": { + const [, ...filters] = filter; + return this._compileLogicalOpAnd(filters); + } + default: + // tslint:disable-next-line:no-unused-expression + filter satisfies never; // exhaustivity check + return () => true; + } } - private _compileInOp(property: string, values: T[]): string { - const compare: (a: T, b: T) => number = this._compare; - const left: string = JSON.stringify(values.sort(compare)); - const right: string = this._compilePropertyReference(property); - - return left + ".indexOf(" + right + ")!==-1"; + private _compileComparisonOp( + operator: (a: T, b: T) => boolean, + property: FilterKey, + right: T, + checkType: boolean, + ): FilterFunction { + return x => { + const left = x[property]; + return (checkType ? typeof left === typeof right : true) && operator(left, right) + }; } - private _compileLogicalOp(filters: FilterExpression[], operator: string): string { - const compile: (filter: FilterExpression) => string = this._compile.bind(this); - - return filters.map(compile).join(operator); + private _compileInOp(property: FilterKey, values: Set): FilterFunction { + return (x) => values.has(x[property]); } - private _compileNegation(expression: string): string { - return "!(" + expression + ")"; + private _compileLogicalOpAnd( + filters: FilterExpression[], + ): FilterFunction { + const functions = filters.map(filter => this.createFilter(filter)); + return x => functions.every(f => f(x)); } - private _compilePropertyReference(property: string): string { - return "node[" + JSON.stringify(property) + "]"; + private _compileNegation(f: FilterFunction): FilterFunction { + return (x) => !f(x); } }