From 5be45fd8c4a5c8aa67c16db3a9232eaf838b0b99 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 3 Jun 2024 06:29:58 -0700 Subject: [PATCH 1/2] add failing test --- test/unit.test.js | 4 ++++ test/unit/string-concat-chain/input.js | 24 +++++++++++++++++++ ..._7_8_9_10_11_12_13_14_15_16_17_18_19_20.js | 1 + test/unit/string-concat-chain/output.js | 5 ++++ 4 files changed, 34 insertions(+) create mode 100644 test/unit/string-concat-chain/input.js create mode 100644 test/unit/string-concat-chain/lib/1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20.js create mode 100644 test/unit/string-concat-chain/output.js diff --git a/test/unit.test.js b/test/unit.test.js index d54a50fd..12609ce7 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -129,6 +129,7 @@ for (const { testName, isRoot } of unitTests) { inputFileNames.push('input-2.js', 'input-3.js', 'input-4.js'); } + const startTime = Date.now(); const { fileList, reasons } = await nodeFileTrace( inputFileNames.map((file) => join(unitPath, file)), { @@ -155,6 +156,9 @@ for (const { testName, isRoot } of unitTests) { }, ); + const totalTime = Date.now() - startTime; + expect(totalTime).toBeLessThan(1000); + const normalizeFilesRoot = (f) => (isRoot ? relative(join('./', __dirname, '..'), f) : f).replace( /\\/g, diff --git a/test/unit/string-concat-chain/input.js b/test/unit/string-concat-chain/input.js new file mode 100644 index 00000000..40b0d440 --- /dev/null +++ b/test/unit/string-concat-chain/input.js @@ -0,0 +1,24 @@ +var str = './lib/' + .concat('1') + .concat('_', '2') + .concat('_', '3') + .concat('_', '4') + .concat('_', '5') + .concat('_', '6') + .concat('_', '7') + .concat('_', '8') + .concat('_', '9') + .concat('_', '10') + .concat('_', '11') + .concat('_', '12') + .concat('_', '13') + .concat('_', '14') + .concat('_', '15') + .concat('_', '16') + .concat('_', '17') + .concat('_', '18') + .concat('_', '19') + .concat('_', '20') + .concat('.js'); + +require(str); \ No newline at end of file diff --git a/test/unit/string-concat-chain/lib/1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20.js b/test/unit/string-concat-chain/lib/1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20.js new file mode 100644 index 00000000..1c238099 --- /dev/null +++ b/test/unit/string-concat-chain/lib/1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20.js @@ -0,0 +1 @@ +module.exports = { file: 'file' } \ No newline at end of file diff --git a/test/unit/string-concat-chain/output.js b/test/unit/string-concat-chain/output.js new file mode 100644 index 00000000..ff2b4f22 --- /dev/null +++ b/test/unit/string-concat-chain/output.js @@ -0,0 +1,5 @@ +[ + "package.json", + "test/unit/string-concat-chain/input.js", + "test/unit/string-concat-chain/lib/1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20.js" +] \ No newline at end of file From d198252b0977fa9902e19528b179d00c586e84c2 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 3 Jun 2024 09:56:27 -0700 Subject: [PATCH 2/2] fix: add cache --- src/utils/safe-stringify.ts | 34 ++++++++++++++++++++++++++++++++++ src/utils/static-eval.ts | 27 ++++++++++++++++++--------- test/unit.test.js | 2 ++ 3 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 src/utils/safe-stringify.ts diff --git a/src/utils/safe-stringify.ts b/src/utils/safe-stringify.ts new file mode 100644 index 00000000..a5a644df --- /dev/null +++ b/src/utils/safe-stringify.ts @@ -0,0 +1,34 @@ +const replacer = (key: string, value: unknown) => { + if (typeof value === 'object') { + if (key === 'globalThis') { + return '[globalThis]'; + } + if (key === 'global') { + return '[global]'; + } + if (key === 'GLOBAL') { + return '[GLOBAL]'; + } + if (key === 'process') { + return '[process]'; + } + if (key === 'win32') { + // path.win32 + return '[win32]'; + } + if (key === 'posix') { + // path.posix + return '[posix]'; + } + } + if (typeof value === 'function') { + return '[Function]'; + } + if (typeof value === 'bigint') { + return '[bigint]'; + } + return value; +}; +export function safeStringify(obj: unknown) { + return JSON.stringify(obj, replacer); +} diff --git a/src/utils/static-eval.ts b/src/utils/static-eval.ts index 136d64e3..fcfc813b 100644 --- a/src/utils/static-eval.ts +++ b/src/utils/static-eval.ts @@ -1,29 +1,38 @@ +import { safeStringify } from './safe-stringify'; import { EvaluatedValue, StaticValue, ConditionalValue, Node } from './types'; -import { URL } from 'url'; -type Walk = (node: Node) => EvaluatedValue; +type Walk = (node: Node) => Promise; type State = { computeBranches: boolean; vars: Record }; +const walkCache = new Map(); + export async function evaluate( - ast: Node, - vars = {}, - computeBranches = true, + ast: Readonly, + vars: Readonly> = {}, + computeBranches: Readonly = true, ): Promise { const state: State = { computeBranches, vars, }; + const stateCacheKey = safeStringify(state); return walk(ast); // walk returns: // 1. Single known value: { value: value } // 2. Conditional value: { test, then, else } // 3. Unknown value: undefined - function walk(node: Node) { + async function walk(node: Node) { + const walkCacheKey = stateCacheKey + safeStringify(node); + let result = walkCache.get(walkCacheKey); + if (result) { + return result; + } const visitor = visitors[node.type]; if (visitor) { - return visitor.call(state, node, walk); + result = await visitor.call(state, node, walk); } - return undefined; + walkCache.set(walkCacheKey, result); + return result; } } @@ -359,7 +368,7 @@ const visitors: Record< if (typeof obj.value === 'string' && node.property.name === 'concat') { return { value: { - [FUNCTION]: (...args: string[]) => obj.value.concat(args), + [FUNCTION]: (...args: string[]) => obj.value.concat(...args), }, }; } diff --git a/test/unit.test.js b/test/unit.test.js index 12609ce7..b329a965 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -8,6 +8,8 @@ const stat = gracefulFS.promises.stat; const readlink = gracefulFS.promises.readlink; const readFile = gracefulFS.promises.readFile; +jest.setTimeout(5_000); + global._unit = true; const nodeGypTests = [